528 lines
15 KiB
C++
528 lines
15 KiB
C++
// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
|
|
// reserved. Use of this source code is governed by a BSD-style license that can
|
|
// be found in the LICENSE file.
|
|
|
|
#include "libcef/renderer/frame_impl.h"
|
|
|
|
#include "base/compiler_specific.h"
|
|
|
|
// Enable deprecation warnings on Windows. See http://crbug.com/585142.
|
|
#if defined(OS_WIN)
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic error "-Wdeprecated-declarations"
|
|
#else
|
|
#pragma warning(push)
|
|
#pragma warning(default : 4996)
|
|
#endif
|
|
#endif
|
|
|
|
#include "libcef/common/cef_messages.h"
|
|
#include "libcef/common/content_client.h"
|
|
#include "libcef/common/net/http_header_utils.h"
|
|
#include "libcef/common/process_message_impl.h"
|
|
#include "libcef/common/request_impl.h"
|
|
#include "libcef/common/response_manager.h"
|
|
#include "libcef/renderer/blink_glue.h"
|
|
#include "libcef/renderer/browser_impl.h"
|
|
#include "libcef/renderer/dom_document_impl.h"
|
|
#include "libcef/renderer/render_frame_util.h"
|
|
#include "libcef/renderer/render_urlrequest_impl.h"
|
|
#include "libcef/renderer/thread_util.h"
|
|
#include "libcef/renderer/v8_impl.h"
|
|
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "content/public/renderer/render_view.h"
|
|
#include "content/renderer/render_frame_impl.h"
|
|
#include "third_party/blink/public/platform/web_data.h"
|
|
#include "third_party/blink/public/platform/web_string.h"
|
|
#include "third_party/blink/public/platform/web_url.h"
|
|
#include "third_party/blink/public/web/blink.h"
|
|
#include "third_party/blink/public/web/web_document.h"
|
|
#include "third_party/blink/public/web/web_document_loader.h"
|
|
#include "third_party/blink/public/web/web_frame_content_dumper.h"
|
|
#include "third_party/blink/public/web/web_local_frame.h"
|
|
#include "third_party/blink/public/web/web_navigation_control.h"
|
|
#include "third_party/blink/public/web/web_script_source.h"
|
|
#include "third_party/blink/public/web/web_view.h"
|
|
|
|
CefFrameImpl::CefFrameImpl(CefBrowserImpl* browser,
|
|
blink::WebLocalFrame* frame,
|
|
int64_t frame_id)
|
|
: browser_(browser),
|
|
frame_(frame),
|
|
frame_id_(frame_id),
|
|
response_manager_(new CefResponseManager) {}
|
|
|
|
CefFrameImpl::~CefFrameImpl() {}
|
|
|
|
bool CefFrameImpl::IsValid() {
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
return (frame_ != nullptr);
|
|
}
|
|
|
|
void CefFrameImpl::Undo() {
|
|
ExecuteCommand("Undo");
|
|
}
|
|
|
|
void CefFrameImpl::Redo() {
|
|
ExecuteCommand("Redo");
|
|
}
|
|
|
|
void CefFrameImpl::Cut() {
|
|
ExecuteCommand("Cut");
|
|
}
|
|
|
|
void CefFrameImpl::Copy() {
|
|
ExecuteCommand("Copy");
|
|
}
|
|
|
|
void CefFrameImpl::Paste() {
|
|
ExecuteCommand("Paste");
|
|
}
|
|
|
|
void CefFrameImpl::Delete() {
|
|
ExecuteCommand("Delete");
|
|
}
|
|
|
|
void CefFrameImpl::SelectAll() {
|
|
ExecuteCommand("SelectAll");
|
|
}
|
|
|
|
void CefFrameImpl::ViewSource() {
|
|
NOTREACHED() << "ViewSource cannot be called from the renderer process";
|
|
}
|
|
|
|
void CefFrameImpl::GetSource(CefRefPtr<CefStringVisitor> visitor) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
if (frame_) {
|
|
const CefString& content =
|
|
std::string(blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8());
|
|
visitor->Visit(content);
|
|
}
|
|
}
|
|
|
|
void CefFrameImpl::GetText(CefRefPtr<CefStringVisitor> visitor) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
if (frame_) {
|
|
const CefString& content = blink_glue::DumpDocumentText(frame_);
|
|
visitor->Visit(content);
|
|
}
|
|
}
|
|
|
|
void CefFrameImpl::LoadRequest(CefRefPtr<CefRequest> request) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
if (!frame_)
|
|
return;
|
|
|
|
CefMsg_LoadRequest_Params params;
|
|
params.url = GURL(std::string(request->GetURL()));
|
|
params.method = request->GetMethod();
|
|
params.site_for_cookies =
|
|
GURL(std::string(request->GetFirstPartyForCookies()));
|
|
|
|
CefRequest::HeaderMap headerMap;
|
|
request->GetHeaderMap(headerMap);
|
|
if (!headerMap.empty())
|
|
params.headers = HttpHeaderUtils::GenerateHeaders(headerMap);
|
|
|
|
CefRefPtr<CefPostData> postData = request->GetPostData();
|
|
if (postData.get()) {
|
|
CefPostDataImpl* impl = static_cast<CefPostDataImpl*>(postData.get());
|
|
params.upload_data = new net::UploadData();
|
|
impl->Get(*params.upload_data.get());
|
|
}
|
|
|
|
params.load_flags = request->GetFlags();
|
|
|
|
OnLoadRequest(params);
|
|
}
|
|
|
|
void CefFrameImpl::LoadURL(const CefString& url) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
if (!frame_)
|
|
return;
|
|
|
|
CefMsg_LoadRequest_Params params;
|
|
params.url = GURL(url.ToString());
|
|
params.method = "GET";
|
|
|
|
OnLoadRequest(params);
|
|
}
|
|
|
|
void CefFrameImpl::ExecuteJavaScript(const CefString& jsCode,
|
|
const CefString& scriptUrl,
|
|
int startLine) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
if (jsCode.empty())
|
|
return;
|
|
if (startLine < 1)
|
|
startLine = 1;
|
|
|
|
if (frame_) {
|
|
GURL gurl = GURL(scriptUrl.ToString());
|
|
frame_->ExecuteScript(blink::WebScriptSource(
|
|
blink::WebString::FromUTF16(jsCode.ToString16()), gurl, startLine));
|
|
}
|
|
}
|
|
|
|
bool CefFrameImpl::IsMain() {
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
if (frame_)
|
|
return (frame_->Parent() == nullptr);
|
|
return false;
|
|
}
|
|
|
|
bool CefFrameImpl::IsFocused() {
|
|
CEF_REQUIRE_RT_RETURN(false);
|
|
|
|
if (frame_ && frame_->View())
|
|
return (frame_->View()->FocusedFrame() == frame_);
|
|
return false;
|
|
}
|
|
|
|
CefString CefFrameImpl::GetName() {
|
|
CefString name;
|
|
CEF_REQUIRE_RT_RETURN(name);
|
|
|
|
if (frame_)
|
|
name = render_frame_util::GetName(frame_);
|
|
return name;
|
|
}
|
|
|
|
int64 CefFrameImpl::GetIdentifier() {
|
|
CEF_REQUIRE_RT_RETURN(0);
|
|
|
|
return frame_id_;
|
|
}
|
|
|
|
CefRefPtr<CefFrame> CefFrameImpl::GetParent() {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
|
|
if (frame_) {
|
|
blink::WebFrame* parent = frame_->Parent();
|
|
if (parent && parent->IsWebLocalFrame())
|
|
return browser_->GetWebFrameImpl(parent->ToWebLocalFrame()).get();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CefString CefFrameImpl::GetURL() {
|
|
CefString url;
|
|
CEF_REQUIRE_RT_RETURN(url);
|
|
|
|
if (frame_) {
|
|
GURL gurl = frame_->GetDocument().Url();
|
|
url = gurl.spec();
|
|
}
|
|
return url;
|
|
}
|
|
|
|
CefRefPtr<CefBrowser> CefFrameImpl::GetBrowser() {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
|
|
return browser_;
|
|
}
|
|
|
|
CefRefPtr<CefV8Context> CefFrameImpl::GetV8Context() {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
|
|
if (frame_) {
|
|
v8::Isolate* isolate = blink::MainThreadIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
return new CefV8ContextImpl(isolate, frame_->MainWorldScriptContext());
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void CefFrameImpl::VisitDOM(CefRefPtr<CefDOMVisitor> visitor) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
|
|
if (!frame_)
|
|
return;
|
|
|
|
// Create a CefDOMDocumentImpl object that is valid only for the scope of this
|
|
// method.
|
|
CefRefPtr<CefDOMDocumentImpl> documentImpl;
|
|
const blink::WebDocument& document = frame_->GetDocument();
|
|
if (!document.IsNull())
|
|
documentImpl = new CefDOMDocumentImpl(browser_, frame_);
|
|
|
|
visitor->Visit(documentImpl.get());
|
|
|
|
if (documentImpl.get())
|
|
documentImpl->Detach();
|
|
}
|
|
|
|
CefRefPtr<CefURLRequest> CefFrameImpl::CreateURLRequest(
|
|
CefRefPtr<CefRequest> request,
|
|
CefRefPtr<CefURLRequestClient> client) {
|
|
CEF_REQUIRE_RT_RETURN(nullptr);
|
|
|
|
if (!request || !client || !frame_)
|
|
return nullptr;
|
|
|
|
CefRefPtr<CefRenderURLRequest> impl =
|
|
new CefRenderURLRequest(this, request, client);
|
|
if (impl->Start())
|
|
return impl.get();
|
|
return nullptr;
|
|
}
|
|
|
|
void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
|
|
CefRefPtr<CefProcessMessage> message) {
|
|
Cef_Request_Params params;
|
|
CefProcessMessageImpl* impl =
|
|
static_cast<CefProcessMessageImpl*>(message.get());
|
|
if (impl->CopyTo(params)) {
|
|
SendProcessMessage(target_process, params.name, ¶ms.arguments, true);
|
|
}
|
|
}
|
|
|
|
blink::WebURLLoaderFactory* CefFrameImpl::GetURLLoaderFactory() {
|
|
CEF_REQUIRE_RT();
|
|
if (!url_loader_factory_ && frame_) {
|
|
auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_);
|
|
if (render_frame) {
|
|
url_loader_factory_ = render_frame->CreateURLLoaderFactory();
|
|
}
|
|
}
|
|
return url_loader_factory_.get();
|
|
}
|
|
|
|
void CefFrameImpl::OnAttached() {
|
|
Send(new CefHostMsg_FrameAttached(MSG_ROUTING_NONE));
|
|
}
|
|
|
|
bool CefFrameImpl::OnMessageReceived(const IPC::Message& message) {
|
|
bool handled = true;
|
|
IPC_BEGIN_MESSAGE_MAP(CefFrameImpl, message)
|
|
IPC_MESSAGE_HANDLER(CefMsg_Request, OnRequest)
|
|
IPC_MESSAGE_HANDLER(CefMsg_Response, OnResponse)
|
|
IPC_MESSAGE_HANDLER(CefMsg_ResponseAck, OnResponseAck)
|
|
IPC_MESSAGE_HANDLER(CefMsg_LoadRequest, OnLoadRequest)
|
|
IPC_MESSAGE_HANDLER(CefMsg_DidStopLoading, OnDidStopLoading)
|
|
IPC_MESSAGE_UNHANDLED(handled = false)
|
|
IPC_END_MESSAGE_MAP()
|
|
return handled;
|
|
}
|
|
|
|
void CefFrameImpl::OnDidFinishLoad() {
|
|
// Ignore notifications from the embedded frame hosting a mime-type plugin.
|
|
// We'll eventually receive a notification from the owner frame.
|
|
if (blink_glue::HasPluginFrameOwner(frame_))
|
|
return;
|
|
|
|
blink::WebDocumentLoader* dl = frame_->GetDocumentLoader();
|
|
const int http_status_code = dl->GetResponse().HttpStatusCode();
|
|
Send(new CefHostMsg_DidFinishLoad(MSG_ROUTING_NONE, dl->GetUrl(),
|
|
http_status_code));
|
|
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
if (app) {
|
|
CefRefPtr<CefRenderProcessHandler> handler = app->GetRenderProcessHandler();
|
|
if (handler) {
|
|
CefRefPtr<CefLoadHandler> load_handler = handler->GetLoadHandler();
|
|
if (load_handler) {
|
|
load_handler->OnLoadEnd(browser_, this, http_status_code);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CefFrameImpl::OnDraggableRegionsChanged() {
|
|
blink::WebVector<blink::WebDraggableRegion> webregions =
|
|
frame_->GetDocument().DraggableRegions();
|
|
std::vector<Cef_DraggableRegion_Params> regions;
|
|
for (size_t i = 0; i < webregions.size(); ++i) {
|
|
Cef_DraggableRegion_Params region;
|
|
auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_);
|
|
render_frame->ConvertViewportToWindow(&webregions[i].bounds);
|
|
region.bounds = webregions[i].bounds;
|
|
region.draggable = webregions[i].draggable;
|
|
regions.push_back(region);
|
|
}
|
|
Send(new CefHostMsg_UpdateDraggableRegions(MSG_ROUTING_NONE, regions));
|
|
}
|
|
|
|
void CefFrameImpl::OnDetached() {
|
|
// The browser may hold the last reference to |this|. Take a reference here to
|
|
// keep |this| alive until after this method returns.
|
|
CefRefPtr<CefFrameImpl> self = this;
|
|
|
|
browser_->FrameDetached(frame_id_);
|
|
|
|
browser_ = nullptr;
|
|
frame_ = nullptr;
|
|
url_loader_factory_.reset();
|
|
response_manager_.reset();
|
|
}
|
|
|
|
void CefFrameImpl::ExecuteCommand(const std::string& command) {
|
|
CEF_REQUIRE_RT_RETURN_VOID();
|
|
if (frame_)
|
|
frame_->ExecuteCommand(blink::WebString::FromUTF8(command));
|
|
}
|
|
|
|
void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
|
|
const std::string& name,
|
|
base::ListValue* arguments,
|
|
bool user_initiated) {
|
|
DCHECK_EQ(PID_BROWSER, target_process);
|
|
DCHECK(!name.empty());
|
|
|
|
if (!frame_)
|
|
return;
|
|
|
|
Cef_Request_Params params;
|
|
params.name = name;
|
|
if (arguments)
|
|
params.arguments.Swap(arguments);
|
|
params.user_initiated = user_initiated;
|
|
params.request_id = -1;
|
|
params.expect_response = false;
|
|
|
|
Send(new CefHostMsg_Request(MSG_ROUTING_NONE, params));
|
|
}
|
|
|
|
void CefFrameImpl::Send(IPC::Message* message) {
|
|
if (!frame_) {
|
|
delete message;
|
|
return;
|
|
}
|
|
|
|
auto render_frame = content::RenderFrame::FromWebFrame(frame_);
|
|
message->set_routing_id(render_frame->GetRoutingID());
|
|
render_frame->Send(message);
|
|
}
|
|
|
|
void CefFrameImpl::OnRequest(const Cef_Request_Params& params) {
|
|
DCHECK(browser_);
|
|
DCHECK(frame_);
|
|
|
|
bool success = false;
|
|
std::string response;
|
|
bool expect_response_ack = false;
|
|
|
|
TRACE_EVENT2("cef", "CefBrowserImpl::OnRequest", "request_id",
|
|
params.request_id, "expect_response",
|
|
params.expect_response ? 1 : 0);
|
|
|
|
if (params.user_initiated) {
|
|
// Give the user a chance to handle the request.
|
|
CefRefPtr<CefApp> app = CefContentClient::Get()->application();
|
|
if (app.get()) {
|
|
CefRefPtr<CefRenderProcessHandler> handler =
|
|
app->GetRenderProcessHandler();
|
|
if (handler.get()) {
|
|
CefRefPtr<CefProcessMessageImpl> message(new CefProcessMessageImpl(
|
|
const_cast<Cef_Request_Params*>(¶ms), false, true));
|
|
success = handler->OnProcessMessageReceived(browser_, this, PID_BROWSER,
|
|
message.get());
|
|
message->Detach(nullptr);
|
|
}
|
|
}
|
|
} else if (params.name == "execute-code") {
|
|
// Execute code.
|
|
DCHECK_EQ(params.arguments.GetSize(), (size_t)4);
|
|
|
|
bool is_javascript = false;
|
|
std::string code, script_url;
|
|
int script_start_line = 0;
|
|
|
|
params.arguments.GetBoolean(0, &is_javascript);
|
|
params.arguments.GetString(1, &code);
|
|
DCHECK(!code.empty());
|
|
params.arguments.GetString(2, &script_url);
|
|
params.arguments.GetInteger(3, &script_start_line);
|
|
DCHECK_GE(script_start_line, 0);
|
|
|
|
if (is_javascript) {
|
|
frame_->ExecuteScript(
|
|
blink::WebScriptSource(blink::WebString::FromUTF8(code),
|
|
GURL(script_url), script_start_line));
|
|
success = true;
|
|
} else {
|
|
// TODO(cef): implement support for CSS code.
|
|
NOTIMPLEMENTED();
|
|
}
|
|
} else if (params.name == "execute-command") {
|
|
// Execute command.
|
|
DCHECK_EQ(params.arguments.GetSize(), (size_t)1);
|
|
|
|
std::string command;
|
|
|
|
params.arguments.GetString(0, &command);
|
|
DCHECK(!command.empty());
|
|
|
|
if (base::LowerCaseEqualsASCII(command, "getsource")) {
|
|
response = blink::WebFrameContentDumper::DumpAsMarkup(frame_).Utf8();
|
|
success = true;
|
|
} else if (base::LowerCaseEqualsASCII(command, "gettext")) {
|
|
response = blink_glue::DumpDocumentText(frame_);
|
|
success = true;
|
|
} else if (frame_->ExecuteCommand(blink::WebString::FromUTF8(command))) {
|
|
success = true;
|
|
}
|
|
} else {
|
|
// Invalid request.
|
|
NOTREACHED();
|
|
}
|
|
|
|
if (params.expect_response) {
|
|
DCHECK_GE(params.request_id, 0);
|
|
|
|
// Send a response to the browser.
|
|
Cef_Response_Params response_params;
|
|
response_params.request_id = params.request_id;
|
|
response_params.success = success;
|
|
response_params.response = response;
|
|
response_params.expect_response_ack = expect_response_ack;
|
|
Send(new CefHostMsg_Response(MSG_ROUTING_NONE, response_params));
|
|
}
|
|
}
|
|
|
|
void CefFrameImpl::OnResponse(const Cef_Response_Params& params) {
|
|
response_manager_->RunHandler(params);
|
|
if (params.expect_response_ack)
|
|
Send(new CefHostMsg_ResponseAck(MSG_ROUTING_NONE, params.request_id));
|
|
}
|
|
|
|
void CefFrameImpl::OnResponseAck(int request_id) {
|
|
response_manager_->RunAckHandler(request_id);
|
|
}
|
|
|
|
void CefFrameImpl::OnDidStopLoading() {
|
|
// We should only receive this notification for the highest-level LocalFrame
|
|
// in this frame's in-process subtree. If there are multiple of these for the
|
|
// same browser then the other occurrences will be discarded in
|
|
// OnLoadingStateChange.
|
|
DCHECK(frame_->IsLocalRoot());
|
|
browser_->OnLoadingStateChange(false);
|
|
}
|
|
|
|
void CefFrameImpl::OnLoadRequest(const CefMsg_LoadRequest_Params& params) {
|
|
DCHECK(frame_);
|
|
|
|
blink::WebURLRequest request;
|
|
CefRequestImpl::Get(params, request);
|
|
|
|
frame_->StartNavigation(request);
|
|
}
|
|
|
|
// Enable deprecation warnings on Windows. See http://crbug.com/585142.
|
|
#if defined(OS_WIN)
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#else
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|