// Copyright (c) 2012 The Chromium Embedded Framework Authors. // Portions copyright (c) 2011 The Chromium 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/browser_impl.h" #include #include #include "libcef/common/cef_messages.h" #include "libcef/common/content_client.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/content_renderer_client.h" #include "libcef/renderer/dom_document_impl.h" #include "libcef/renderer/render_frame_util.h" #include "libcef/renderer/thread_util.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_view.h" #include "content/renderer/navigation_state_impl.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/platform/web_url_error.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_frame.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_node.h" #include "third_party/blink/public/web/web_script_source.h" #include "third_party/blink/public/web/web_security_policy.h" #include "third_party/blink/public/web/web_view.h" using blink::WebFrame; using blink::WebLocalFrame; using blink::WebScriptSource; using blink::WebString; using blink::WebURL; using blink::WebView; // CefBrowserImpl static methods. // ----------------------------------------------------------------------------- // static CefRefPtr CefBrowserImpl::GetBrowserForView( content::RenderView* view) { return CefContentRendererClient::Get()->GetBrowserForView(view); } // static CefRefPtr CefBrowserImpl::GetBrowserForMainFrame( blink::WebFrame* frame) { return CefContentRendererClient::Get()->GetBrowserForMainFrame(frame); } // CefBrowser methods. // ----------------------------------------------------------------------------- CefRefPtr CefBrowserImpl::GetHost() { NOTREACHED() << "GetHost cannot be called from the render process"; return nullptr; } bool CefBrowserImpl::CanGoBack() { CEF_REQUIRE_RT_RETURN(false); return blink_glue::CanGoBack(render_view()->GetWebView()); } void CefBrowserImpl::GoBack() { CEF_REQUIRE_RT_RETURN_VOID(); blink_glue::GoBack(render_view()->GetWebView()); } bool CefBrowserImpl::CanGoForward() { CEF_REQUIRE_RT_RETURN(false); return blink_glue::CanGoForward(render_view()->GetWebView()); } void CefBrowserImpl::GoForward() { CEF_REQUIRE_RT_RETURN_VOID(); blink_glue::GoForward(render_view()->GetWebView()); } bool CefBrowserImpl::IsLoading() { CEF_REQUIRE_RT_RETURN(false); if (render_view()->GetWebView()) { blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame) return main_frame->ToWebLocalFrame()->IsLoading(); } return false; } void CefBrowserImpl::Reload() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { main_frame->ToWebLocalFrame()->Reload(blink::WebFrameLoadType::kReload); } } } void CefBrowserImpl::ReloadIgnoreCache() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { main_frame->ToWebLocalFrame()->Reload( blink::WebFrameLoadType::kReloadBypassingCache); } } } void CefBrowserImpl::StopLoad() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { main_frame->ToWebLocalFrame()->StopLoading(); } } } int CefBrowserImpl::GetIdentifier() { CEF_REQUIRE_RT_RETURN(0); return browser_id(); } bool CefBrowserImpl::IsSame(CefRefPtr that) { CEF_REQUIRE_RT_RETURN(false); CefBrowserImpl* impl = static_cast(that.get()); return (impl == this); } bool CefBrowserImpl::IsPopup() { CEF_REQUIRE_RT_RETURN(false); return is_popup(); } bool CefBrowserImpl::HasDocument() { CEF_REQUIRE_RT_RETURN(false); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { return !main_frame->ToWebLocalFrame()->GetDocument().IsNull(); } } return false; } CefRefPtr CefBrowserImpl::GetMainFrame() { CEF_REQUIRE_RT_RETURN(nullptr); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { return GetWebFrameImpl(main_frame->ToWebLocalFrame()).get(); } } return nullptr; } CefRefPtr CefBrowserImpl::GetFocusedFrame() { CEF_REQUIRE_RT_RETURN(nullptr); if (render_view()->GetWebView() && render_view()->GetWebView()->FocusedFrame()) { return GetWebFrameImpl(render_view()->GetWebView()->FocusedFrame()).get(); } return nullptr; } CefRefPtr CefBrowserImpl::GetFrame(int64 identifier) { CEF_REQUIRE_RT_RETURN(nullptr); return GetWebFrameImpl(identifier).get(); } CefRefPtr CefBrowserImpl::GetFrame(const CefString& name) { CEF_REQUIRE_RT_RETURN(nullptr); blink::WebView* web_view = render_view()->GetWebView(); if (web_view) { const blink::WebString& frame_name = blink::WebString::FromUTF16(name.ToString16()); // Search by assigned frame name (Frame::name). WebFrame* frame = web_view->MainFrame(); if (frame && frame->IsWebLocalFrame()) frame = frame->ToWebLocalFrame()->FindFrameByName(frame_name); if (!frame) { // Search by unique frame name (Frame::uniqueName). const std::string& searchname = name; for (WebFrame* cur_frame = web_view->MainFrame(); cur_frame; cur_frame = cur_frame->TraverseNext()) { if (cur_frame->IsWebLocalFrame() && render_frame_util::GetName(cur_frame->ToWebLocalFrame()) == searchname) { frame = cur_frame; break; } } } if (frame && frame->IsWebLocalFrame()) return GetWebFrameImpl(frame->ToWebLocalFrame()).get(); } return nullptr; } size_t CefBrowserImpl::GetFrameCount() { CEF_REQUIRE_RT_RETURN(0); int count = 0; if (render_view()->GetWebView()) { for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame; frame = frame->TraverseNext()) { count++; } } return count; } void CefBrowserImpl::GetFrameIdentifiers(std::vector& identifiers) { CEF_REQUIRE_RT_RETURN_VOID(); if (identifiers.size() > 0) identifiers.clear(); if (render_view()->GetWebView()) { for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame; frame = frame->TraverseNext()) { if (frame->IsWebLocalFrame()) identifiers.push_back( render_frame_util::GetIdentifier(frame->ToWebLocalFrame())); } } } void CefBrowserImpl::GetFrameNames(std::vector& names) { CEF_REQUIRE_RT_RETURN_VOID(); if (names.size() > 0) names.clear(); if (render_view()->GetWebView()) { for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame; frame = frame->TraverseNext()) { if (frame->IsWebLocalFrame()) names.push_back(render_frame_util::GetName(frame->ToWebLocalFrame())); } } } bool CefBrowserImpl::SendProcessMessage(CefProcessId target_process, CefRefPtr message) { Cef_Request_Params params; CefProcessMessageImpl* impl = static_cast(message.get()); if (impl->CopyTo(params)) { return SendProcessMessage(target_process, params.name, ¶ms.arguments, true); } return false; } // CefBrowserImpl public methods. // ----------------------------------------------------------------------------- CefBrowserImpl::CefBrowserImpl(content::RenderView* render_view, int browser_id, bool is_popup, bool is_windowless) : content::RenderViewObserver(render_view), browser_id_(browser_id), is_popup_(is_popup), is_windowless_(is_windowless) { response_manager_.reset(new CefResponseManager); } CefBrowserImpl::~CefBrowserImpl() {} void CefBrowserImpl::LoadRequest(const CefMsg_LoadRequest_Params& params) { CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (!framePtr.get()) return; WebLocalFrame* web_frame = framePtr->web_frame(); blink::WebURLRequest request; CefRequestImpl::Get(params, request); web_frame->LoadRequest(request); } bool CefBrowserImpl::SendProcessMessage(CefProcessId target_process, const std::string& name, base::ListValue* arguments, bool user_initiated) { DCHECK_EQ(PID_BROWSER, target_process); DCHECK(!name.empty()); Cef_Request_Params params; params.name = name; if (arguments) params.arguments.Swap(arguments); params.frame_id = -1; params.user_initiated = user_initiated; params.request_id = -1; params.expect_response = false; return Send(new CefHostMsg_Request(routing_id(), params)); } CefRefPtr CefBrowserImpl::GetWebFrameImpl( blink::WebLocalFrame* frame) { DCHECK(frame); int64_t frame_id = render_frame_util::GetIdentifier(frame); // Frames are re-used between page loads. Only add the frame to the map once. FrameMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) return it->second; CefRefPtr framePtr(new CefFrameImpl(this, frame)); frames_.insert(std::make_pair(frame_id, framePtr)); const int64_t parent_id = frame->Parent() == NULL ? blink_glue::kInvalidFrameId : frame->Parent()->IsWebLocalFrame() ? render_frame_util::GetIdentifier( frame->Parent()->ToWebLocalFrame()) : blink_glue::kInvalidFrameId; const base::string16& name = base::UTF8ToUTF16(render_frame_util::GetName(frame)); // Notify the browser that the frame has been identified. Send(new CefHostMsg_FrameIdentified(routing_id(), frame_id, parent_id, name)); return framePtr; } CefRefPtr CefBrowserImpl::GetWebFrameImpl(int64_t frame_id) { if (frame_id == blink_glue::kInvalidFrameId) { if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->MainFrame(); if (main_frame && main_frame->IsWebLocalFrame()) { return GetWebFrameImpl(main_frame->ToWebLocalFrame()); } } return nullptr; } // Check if we already know about the frame. FrameMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) return it->second; if (render_view()->GetWebView()) { // Check if the frame exists but we don't know about it yet. for (WebFrame* frame = render_view()->GetWebView()->MainFrame(); frame; frame = frame->TraverseNext()) { if (frame->IsWebLocalFrame() && render_frame_util::GetIdentifier(frame->ToWebLocalFrame()) == frame_id) { return GetWebFrameImpl(frame->ToWebLocalFrame()); } } } return nullptr; } void CefBrowserImpl::AddFrameObject(int64_t frame_id, CefTrackNode* tracked_object) { CefRefPtr manager; if (!frame_objects_.empty()) { FrameObjectMap::const_iterator it = frame_objects_.find(frame_id); if (it != frame_objects_.end()) manager = it->second; } if (!manager.get()) { manager = new CefTrackManager(); frame_objects_.insert(std::make_pair(frame_id, manager)); } manager->Add(tracked_object); } // RenderViewObserver methods. // ----------------------------------------------------------------------------- void CefBrowserImpl::OnDestruct() { // Notify that the browser window has been destroyed. CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) handler->OnBrowserDestroyed(this); } response_manager_.reset(); CefContentRendererClient::Get()->OnBrowserDestroyed(this); } void CefBrowserImpl::DidStopLoading() { OnLoadingStateChange(false); } void CefBrowserImpl::DidFinishLoad(blink::WebLocalFrame* frame) { blink::WebDocumentLoader* dl = frame->GetDocumentLoader(); Send(new CefHostMsg_DidFinishLoad(routing_id(), render_frame_util::GetIdentifier(frame), dl->GetRequest().Url(), !frame->Parent(), dl->GetResponse().HttpStatusCode())); OnLoadEnd(frame); } void CefBrowserImpl::DidStartProvisionalLoad(blink::WebLocalFrame* frame) { // Send the frame creation notification if necessary. GetWebFrameImpl(frame); } void CefBrowserImpl::DidFailProvisionalLoad(blink::WebLocalFrame* frame, const blink::WebURLError& error) { OnLoadError(frame, error); } void CefBrowserImpl::DidCommitProvisionalLoad(blink::WebLocalFrame* frame, bool is_new_navigation) { if (frame->Parent() == nullptr) { OnLoadingStateChange(true); } OnLoadStart(frame); } void CefBrowserImpl::FrameDetached(WebLocalFrame* frame) { int64_t frame_id = render_frame_util::GetIdentifier(frame); if (!frames_.empty()) { // Remove the frame from the map. FrameMap::iterator it = frames_.find(frame_id); if (it != frames_.end()) { it->second->Detach(); frames_.erase(it); } } if (!frame_objects_.empty()) { // Remove any tracked objects associated with the frame. FrameObjectMap::iterator it = frame_objects_.find(frame_id); if (it != frame_objects_.end()) frame_objects_.erase(it); } } void CefBrowserImpl::FocusedNodeChanged(const blink::WebNode& node) { // Notify the handler. CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { if (node.IsNull()) { handler->OnFocusedNodeChanged(this, GetFocusedFrame(), NULL); } else { const blink::WebDocument& document = node.GetDocument(); if (!document.IsNull()) { blink::WebFrame* frame = document.GetFrame(); if (!frame->IsWebLocalFrame()) return; CefRefPtr documentImpl = new CefDOMDocumentImpl(this, frame->ToWebLocalFrame()); handler->OnFocusedNodeChanged( this, GetWebFrameImpl(frame->ToWebLocalFrame()).get(), documentImpl->GetOrCreateNode(node)); documentImpl->Detach(); } } } } } // Based on ExtensionHelper::DraggableRegionsChanged. void CefBrowserImpl::DraggableRegionsChanged(blink::WebLocalFrame* frame) { blink::WebVector webregions = frame->GetDocument().DraggableRegions(); std::vector regions; for (size_t i = 0; i < webregions.size(); ++i) { Cef_DraggableRegion_Params region; render_view()->ConvertViewportToWindowViaWidget(&webregions[i].bounds); region.bounds = webregions[i].bounds; region.draggable = webregions[i].draggable; regions.push_back(region); } Send(new CefHostMsg_UpdateDraggableRegions(routing_id(), regions)); } bool CefBrowserImpl::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CefBrowserImpl, message) IPC_MESSAGE_HANDLER(CefMsg_Request, OnRequest) IPC_MESSAGE_HANDLER(CefMsg_Response, OnResponse) IPC_MESSAGE_HANDLER(CefMsg_ResponseAck, OnResponseAck) IPC_MESSAGE_HANDLER(CefMsg_LoadRequest, LoadRequest) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } // RenderViewObserver::OnMessageReceived message handlers. // ----------------------------------------------------------------------------- void CefBrowserImpl::OnRequest(const Cef_Request_Params& params) { bool success = false; std::string response; bool expect_response_ack = false; TRACE_EVENT2("libcef", "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 = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr message(new CefProcessMessageImpl( const_cast(¶ms), false, true)); success = handler->OnProcessMessageReceived(this, PID_BROWSER, message.get()); message->Detach(nullptr); } } } else if (params.name == "execute-code") { // Execute code. CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (framePtr.get()) { WebLocalFrame* web_frame = framePtr->web_frame(); if (web_frame) { 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) { web_frame->ExecuteScript( 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. CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (framePtr.get()) { WebLocalFrame* web_frame = framePtr->web_frame(); if (web_frame) { 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(web_frame).Utf8(); success = true; } else if (base::LowerCaseEqualsASCII(command, "gettext")) { response = blink_glue::DumpDocumentText(web_frame); success = true; } else if (web_frame->ExecuteCommand( blink::WebString::FromUTF8(command))) { success = true; } } } } else if (params.name == "load-string") { // Load a string. CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (framePtr.get()) { WebLocalFrame* web_frame = framePtr->web_frame(); if (web_frame) { DCHECK_EQ(params.arguments.GetSize(), (size_t)2); std::string string, url; params.arguments.GetString(0, &string); params.arguments.GetString(1, &url); web_frame->LoadHTMLString(string, GURL(url)); } } } 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(routing_id(), response_params)); } } void CefBrowserImpl::OnResponse(const Cef_Response_Params& params) { response_manager_->RunHandler(params); if (params.expect_response_ack) Send(new CefHostMsg_ResponseAck(routing_id(), params.request_id)); } void CefBrowserImpl::OnResponseAck(int request_id) { response_manager_->RunAckHandler(request_id); } void CefBrowserImpl::OnLoadingStateChange(bool isLoading) { CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr load_handler = handler->GetLoadHandler(); if (load_handler.get()) { WebView* web_view = render_view()->GetWebView(); const bool canGoBack = blink_glue::CanGoBack(web_view); const bool canGoForward = blink_glue::CanGoForward(web_view); load_handler->OnLoadingStateChange(this, isLoading, canGoBack, canGoForward); } } } } void CefBrowserImpl::OnLoadStart(blink::WebLocalFrame* frame) { CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr load_handler = handler->GetLoadHandler(); if (load_handler.get()) { CefRefPtr cef_frame = GetWebFrameImpl(frame); load_handler->OnLoadStart(this, cef_frame.get(), TT_EXPLICIT); } } } } void CefBrowserImpl::OnLoadEnd(blink::WebLocalFrame* frame) { CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr load_handler = handler->GetLoadHandler(); if (load_handler.get()) { CefRefPtr cef_frame = GetWebFrameImpl(frame); int httpStatusCode = frame->GetDocumentLoader()->GetResponse().HttpStatusCode(); load_handler->OnLoadEnd(this, cef_frame.get(), httpStatusCode); } } } } void CefBrowserImpl::OnLoadError(blink::WebLocalFrame* frame, const blink::WebURLError& error) { CefRefPtr app = CefContentClient::Get()->application(); if (app.get()) { CefRefPtr handler = app->GetRenderProcessHandler(); if (handler.get()) { CefRefPtr load_handler = handler->GetLoadHandler(); if (load_handler.get()) { CefRefPtr cef_frame = GetWebFrameImpl(frame); const cef_errorcode_t errorCode = static_cast(error.reason()); const std::string& errorText = net::ErrorToString(error.reason()); const GURL& failedUrl = error.url(); load_handler->OnLoadError(this, cef_frame.get(), errorCode, errorText, failedUrl.spec()); } } } }