// 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/response_manager.h" #include "libcef/renderer/content_renderer_client.h" #include "libcef/renderer/dom_document_impl.h" #include "libcef/renderer/thread_util.h" #include "libcef/renderer/webkit_glue.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.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/render_view_impl.h" #include "net/http/http_util.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" #include "third_party/WebKit/public/web/WebDataSource.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebNode.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebView.h" #include "webkit/glue/webkit_glue.h" using blink::WebFrame; using blink::WebScriptSource; using blink::WebString; using blink::WebURL; using blink::WebView; namespace { const int64 kInvalidFrameId = -1; blink::WebString FilePathStringToWebString( const base::FilePath::StringType& str) { #if defined(OS_POSIX) return base::WideToUTF16Hack(base::SysNativeMBToWide(str)); #elif defined(OS_WIN) return base::WideToUTF16Hack(str); #endif } } // namespace // 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 NULL; } bool CefBrowserImpl::CanGoBack() { CEF_REQUIRE_RT_RETURN(false); return webkit_glue::CanGoBack(render_view()->GetWebView()); } void CefBrowserImpl::GoBack() { CEF_REQUIRE_RT_RETURN_VOID(); webkit_glue::GoBack(render_view()->GetWebView()); } bool CefBrowserImpl::CanGoForward() { CEF_REQUIRE_RT_RETURN(false); return webkit_glue::CanGoForward(render_view()->GetWebView()); } void CefBrowserImpl::GoForward() { CEF_REQUIRE_RT_RETURN_VOID(); webkit_glue::GoForward(render_view()->GetWebView()); } bool CefBrowserImpl::IsLoading() { CEF_REQUIRE_RT_RETURN(false); if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) return render_view()->GetWebView()->mainFrame()->isLoading(); return false; } void CefBrowserImpl::Reload() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) render_view()->GetWebView()->mainFrame()->reload(false); } void CefBrowserImpl::ReloadIgnoreCache() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) render_view()->GetWebView()->mainFrame()->reload(true); } void CefBrowserImpl::StopLoad() { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) render_view()->GetWebView()->mainFrame()->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() && render_view()->GetWebView()->mainFrame()) return !render_view()->GetWebView()->mainFrame()->document().isNull(); return false; } CefRefPtr CefBrowserImpl::GetMainFrame() { CEF_REQUIRE_RT_RETURN(NULL); if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) return GetWebFrameImpl(render_view()->GetWebView()->mainFrame()).get(); return NULL; } CefRefPtr CefBrowserImpl::GetFocusedFrame() { CEF_REQUIRE_RT_RETURN(NULL); if (render_view()->GetWebView() && render_view()->GetWebView()->focusedFrame()) { return GetWebFrameImpl(render_view()->GetWebView()->focusedFrame()).get(); } return NULL; } CefRefPtr CefBrowserImpl::GetFrame(int64 identifier) { CEF_REQUIRE_RT_RETURN(NULL); return GetWebFrameImpl(identifier).get(); } CefRefPtr CefBrowserImpl::GetFrame(const CefString& name) { CEF_REQUIRE_RT_RETURN(NULL); if (render_view()->GetWebView()) { WebFrame* frame = render_view()->GetWebView()->findFrameByName(name.ToString16()); if (frame) return GetWebFrameImpl(frame).get(); } return NULL; } size_t CefBrowserImpl::GetFrameCount() { CEF_REQUIRE_RT_RETURN(0); int count = 0; if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); if (main_frame) { WebFrame* cur = main_frame; do { count++; cur = cur->traverseNext(true); } while (cur != main_frame); } } return count; } void CefBrowserImpl::GetFrameIdentifiers(std::vector& identifiers) { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); if (main_frame) { WebFrame* cur = main_frame; do { identifiers.push_back(cur->identifier()); cur = cur->traverseNext(true); } while (cur != main_frame); } } } void CefBrowserImpl::GetFrameNames(std::vector& names) { CEF_REQUIRE_RT_RETURN_VOID(); if (render_view()->GetWebView()) { WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); if (main_frame) { WebFrame* cur = main_frame; do { names.push_back(CefString(cur->uniqueName().utf8())); cur = cur->traverseNext(true); } while (cur != main_frame); } } } 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_window_rendering_disabled) : content::RenderViewObserver(render_view), browser_id_(browser_id), is_popup_(is_popup), is_window_rendering_disabled_(is_window_rendering_disabled), last_focused_frame_id_(kInvalidFrameId) { 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; WebFrame* web_frame = framePtr->web_frame(); blink::WebURLRequest request(params.url); // DidCreateDataSource checks for this value. request.setRequestorID(-1); if (!params.method.empty()) request.setHTTPMethod(base::ASCIIToUTF16(params.method)); if (params.referrer.is_valid()) { WebString referrer = blink::WebSecurityPolicy::generateReferrerHeader( static_cast(params.referrer_policy), params.url, WebString::fromUTF8(params.referrer.spec())); if (!referrer.isEmpty()) request.setHTTPHeaderField(WebString::fromUTF8("Referer"), referrer); } if (params.first_party_for_cookies.is_valid()) request.setFirstPartyForCookies(params.first_party_for_cookies); if (!params.headers.empty()) { for (net::HttpUtil::HeadersIterator i(params.headers.begin(), params.headers.end(), "\n"); i.GetNext(); ) { request.addHTTPHeaderField(WebString::fromUTF8(i.name()), WebString::fromUTF8(i.values())); } } if (params.upload_data.get()) { base::string16 method = request.httpMethod(); if (method == base::ASCIIToUTF16("GET") || method == base::ASCIIToUTF16("HEAD")) { request.setHTTPMethod(base::ASCIIToUTF16("POST")); } if (request.httpHeaderField( base::ASCIIToUTF16("Content-Type")).length() == 0) { request.setHTTPHeaderField( base::ASCIIToUTF16("Content-Type"), base::ASCIIToUTF16("application/x-www-form-urlencoded")); } blink::WebHTTPBody body; body.initialize(); const ScopedVector& elements = params.upload_data->elements(); ScopedVector::const_iterator it = elements.begin(); for (; it != elements.end(); ++it) { const net::UploadElement& element = **it; if (element.type() == net::UploadElement::TYPE_BYTES) { blink::WebData data; data.assign(element.bytes(), element.bytes_length()); body.appendData(data); } else if (element.type() == net::UploadElement::TYPE_FILE) { body.appendFile(FilePathStringToWebString(element.file_path().value())); } else { NOTREACHED(); } } request.setHTTPBody(body); } 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::WebFrame* frame) { DCHECK(frame); int64 frame_id = frame->identifier(); // 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)); int64 parent_id = frame->parent() == NULL ? kInvalidFrameId : frame->parent()->identifier(); base::string16 name = frame->uniqueName(); // 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 frame_id) { if (frame_id == kInvalidFrameId) { if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) return GetWebFrameImpl(render_view()->GetWebView()->mainFrame()); return NULL; } // 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. WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); if (main_frame) { WebFrame* cur = main_frame; do { if (cur->identifier() == frame_id) return GetWebFrameImpl(cur); cur = cur->traverseNext(true); } while (cur != main_frame); } } return NULL; } void CefBrowserImpl::AddFrameObject(int64 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); } bool CefBrowserImpl::is_swapped_out() const { content::RenderViewImpl* render_view_impl = static_cast(render_view()); return (!render_view_impl || render_view_impl->is_swapped_out()); } // 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(NULL); CefContentRendererClient::Get()->OnBrowserDestroyed(this); } void CefBrowserImpl::DidStartLoading() { OnLoadingStateChange(true); } void CefBrowserImpl::DidStopLoading() { OnLoadingStateChange(false); } void CefBrowserImpl::DidFailLoad( blink::WebFrame* frame, const blink::WebURLError& error) { OnLoadError(frame, error); OnLoadEnd(frame); } void CefBrowserImpl::DidFinishLoad(blink::WebFrame* frame) { blink::WebDataSource* ds = frame->dataSource(); Send(new CefHostMsg_DidFinishLoad(routing_id(), frame->identifier(), ds->request().url(), !frame->parent(), ds->response().httpStatusCode())); OnLoadEnd(frame); } void CefBrowserImpl::DidStartProvisionalLoad(blink::WebFrame* frame) { // Send the frame creation notification if necessary. GetWebFrameImpl(frame); } void CefBrowserImpl::DidFailProvisionalLoad( blink::WebFrame* frame, const blink::WebURLError& error) { OnLoadError(frame, error); } void CefBrowserImpl::DidCommitProvisionalLoad(blink::WebFrame* frame, bool is_new_navigation) { OnLoadStart(frame); } void CefBrowserImpl::FrameDetached(WebFrame* frame) { int64 frame_id = frame->identifier(); 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); } // Notify the browser that the frame has detached. Send(new CefHostMsg_FrameDetached(routing_id(), frame_id)); } 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.document(); if (!document.isNull()) { blink::WebFrame* frame = document.frame(); CefRefPtr documentImpl = new CefDOMDocumentImpl(this, frame); handler->OnFocusedNodeChanged(this, GetWebFrameImpl(frame).get(), documentImpl->GetOrCreateNode(node)); documentImpl->Detach(); } } } } // TODO(cef): This method is being used as a work-around for identifying frame // focus changes. The ideal approach would be implementating delegation from // ChromeClientImpl::focusedFrameChanged(). WebFrame* focused_frame = NULL; // Try to identify the focused frame from the node. if (!node.isNull()) { const blink::WebDocument& document = node.document(); if (!document.isNull()) focused_frame = document.frame(); } if (focused_frame == NULL && render_view()->GetWebView()) { // Try to identify the global focused frame. focused_frame = render_view()->GetWebView()->focusedFrame(); } int64 frame_id = kInvalidFrameId; if (focused_frame != NULL) frame_id = focused_frame->identifier(); // Don't send a message if the focused frame has not changed. if (frame_id == last_focused_frame_id_) return; last_focused_frame_id_ = frame_id; Send(new CefHostMsg_FrameFocusChange(routing_id(), frame_id)); } void CefBrowserImpl::DidCreateDataSource(blink::WebFrame* frame, blink::WebDataSource* ds) { const blink::WebURLRequest& request = ds->request(); if (request.requestorID() == -1) { // Mark the request as browser-initiated so // RenderViewImpl::decidePolicyForNavigation won't attempt to fork it. content::DocumentState* document_state = content::DocumentState::FromDataSource(ds); document_state->set_navigation_state( content::NavigationState::CreateBrowserInitiated(-1, -1, false, content::PAGE_TRANSITION_LINK)); } if (frame->parent() == 0) { GURL url = ds->request().url(); if (!url.is_empty()) { // Notify that the loading URL has changed. Send(new CefHostMsg_LoadingURLChange(routing_id(), url)); } } } 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(NULL); } } } else if (params.name == "execute-code") { // Execute code. CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (framePtr.get()) { WebFrame* 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(base::UTF8ToUTF16(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()) { WebFrame* 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 (LowerCaseEqualsASCII(command, "getsource")) { response = web_frame->contentAsMarkup().utf8(); success = true; } else if (LowerCaseEqualsASCII(command, "gettext")) { response = webkit_glue::DumpDocumentText(web_frame); success = true; } else if (web_frame->executeCommand(base::UTF8ToUTF16(command))) { success = true; } } } } else if (params.name == "load-string") { // Load a string. CefRefPtr framePtr = GetWebFrameImpl(params.frame_id); if (framePtr.get()) { WebFrame* 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) { if (is_swapped_out()) return; 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 = webkit_glue::CanGoBack(web_view); const bool canGoForward = webkit_glue::CanGoForward(web_view); load_handler->OnLoadingStateChange(this, isLoading, canGoBack, canGoForward); } } } } void CefBrowserImpl::OnLoadStart(blink::WebFrame* frame) { if (is_swapped_out()) return; 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()); } } } } void CefBrowserImpl::OnLoadEnd(blink::WebFrame* frame) { if (is_swapped_out()) return; 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->dataSource()->response().httpStatusCode(); load_handler->OnLoadEnd(this, cef_frame.get(), httpStatusCode); } } } } void CefBrowserImpl::OnLoadError(blink::WebFrame* frame, const blink::WebURLError& error) { if (is_swapped_out()) return; 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 = error.localizedDescription.utf8(); const GURL& failedUrl = error.unreachableURL; load_handler->OnLoadError(this, cef_frame.get(), errorCode, errorText, failedUrl.spec()); } } } }