// 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/app_manager.h" #include "libcef/common/cef_messages.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/mojom/frame/frame.mojom-blink.h" #include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.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 visitor) { CEF_REQUIRE_RT_RETURN_VOID(); if (frame_) { const CefString& content = blink_glue::DumpDocumentMarkup(frame_); visitor->Visit(content); } } void CefFrameImpl::GetText(CefRefPtr visitor) { CEF_REQUIRE_RT_RETURN_VOID(); if (frame_) { const CefString& content = blink_glue::DumpDocumentText(frame_); visitor->Visit(content); } } void CefFrameImpl::LoadRequest(CefRefPtr 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 = net::SiteForCookies::FromUrl( GURL(request->GetFirstPartyForCookies().ToString())); CefRequest::HeaderMap headerMap; request->GetHeaderMap(headerMap); if (!headerMap.empty()) params.headers = HttpHeaderUtils::GenerateHeaders(headerMap); CefRefPtr postData = request->GetPostData(); if (postData.get()) { CefPostDataImpl* impl = static_cast(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 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 CefFrameImpl::GetBrowser() { CEF_REQUIRE_RT_RETURN(nullptr); return browser_; } CefRefPtr 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 visitor) { CEF_REQUIRE_RT_RETURN_VOID(); if (!frame_) return; // Create a CefDOMDocumentImpl object that is valid only for the scope of this // method. CefRefPtr 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 CefFrameImpl::CreateURLRequest( CefRefPtr request, CefRefPtr client) { CEF_REQUIRE_RT_RETURN(nullptr); if (!request || !client || !frame_) return nullptr; CefRefPtr impl = new CefRenderURLRequest(this, request, client); if (impl->Start()) return impl.get(); return nullptr; } void CefFrameImpl::SendProcessMessage(CefProcessId target_process, CefRefPtr message) { Cef_Request_Params params; CefProcessMessageImpl* impl = static_cast(message.get()); if (impl->CopyTo(params)) { SendProcessMessage(target_process, params.name, ¶ms.arguments, true); } } std::unique_ptr CefFrameImpl::CreateURLLoader() { CEF_REQUIRE_RT(); if (!frame_) return nullptr; if (!url_loader_factory_) { auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_); if (render_frame) { url_loader_factory_ = render_frame->CreateURLLoaderFactory(); } } if (!url_loader_factory_) return nullptr; // KeepAlive is not supported. mojo::PendingRemote keep_alive_handle = mojo::NullRemote(); return url_loader_factory_->CreateURLLoader( blink::WebURLRequest(), blink_glue::CreateResourceLoadingTaskRunnerHandle(frame_), blink_glue::CreateResourceLoadingMaybeUnfreezableTaskRunnerHandle(frame_), std::move(keep_alive_handle), blink::WebBackForwardCacheLoaderHelper()); } std::unique_ptr CefFrameImpl::CreateResourceLoadInfoNotifierWrapper() { CEF_REQUIRE_RT(); if (frame_) { auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_); if (render_frame) return render_frame->CreateResourceLoadInfoNotifierWrapper(); } return nullptr; } 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_HANDLER(CefMsg_MoveOrResizeStarted, OnMoveOrResizeStarted) 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 app = CefAppManager::Get()->GetApplication(); if (app) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler) { CefRefPtr load_handler = handler->GetLoadHandler(); if (load_handler) { load_handler->OnLoadEnd(browser_, this, http_status_code); } } } } void CefFrameImpl::OnDraggableRegionsChanged() { blink::WebVector webregions = frame_->GetDocument().DraggableRegions(); std::vector 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 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 app = CefAppManager::Get()->GetApplication(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr message(new CefProcessMessageImpl( const_cast(¶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_glue::DumpDocumentMarkup(frame_); 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. browser_->OnLoadingStateChange(false); } void CefFrameImpl::OnMoveOrResizeStarted() { if (frame_) { auto web_view = frame_->View(); if (web_view) web_view->CancelPagePopup(); } } void CefFrameImpl::OnLoadRequest(const CefMsg_LoadRequest_Params& params) { DCHECK(frame_); blink::WebURLRequest request; CefRequestImpl::Get(params, request); blink_glue::StartNavigation(frame_, 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