// 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/browser/browser_host_impl.h" #include #include #include "libcef/browser/browser_context.h" #include "libcef/browser/context.h" #include "libcef/browser/devtools_delegate.h" #include "libcef/browser/navigate_params.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/url_request_context_getter.h" #include "libcef/browser/url_request_context_getter_proxy.h" #include "libcef/common/cef_messages.h" #include "libcef/common/http_header_utils.h" #include "libcef/common/main_delegate.h" #include "libcef/common/process_message_impl.h" #include "libcef/common/request_impl.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/resource_request_info.h" #include "content/public/common/file_chooser_params.h" #include "ui/base/dialogs/selected_file_info.h" namespace { class CreateBrowserHelper { public: CreateBrowserHelper(const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings) : window_info_(windowInfo), client_(client), url_(url), settings_(settings) {} CefWindowInfo window_info_; CefRefPtr client_; CefString url_; CefBrowserSettings settings_; }; void CreateBrowserWithHelper(CreateBrowserHelper* helper) { CefBrowserHost::CreateBrowserSync(helper->window_info_, helper->client_, helper->url_, helper->settings_); delete helper; } // Convert a NativeWebKeyboardEvent to a CefKeyEvent. bool GetCefKeyEvent(const content::NativeWebKeyboardEvent& event, CefKeyEvent& cef_event) { switch (event.type) { case WebKit::WebKeyboardEvent::RawKeyDown: cef_event.type = KEYEVENT_RAWKEYDOWN; break; case WebKit::WebKeyboardEvent::KeyDown: cef_event.type = KEYEVENT_KEYDOWN; break; case WebKit::WebKeyboardEvent::KeyUp: cef_event.type = KEYEVENT_KEYUP; break; case WebKit::WebKeyboardEvent::Char: cef_event.type = KEYEVENT_CHAR; break; default: return false; } cef_event.modifiers = 0; if (event.modifiers & WebKit::WebKeyboardEvent::ShiftKey) cef_event.modifiers |= KEY_SHIFT; if (event.modifiers & WebKit::WebKeyboardEvent::ControlKey) cef_event.modifiers |= KEY_CTRL; if (event.modifiers & WebKit::WebKeyboardEvent::AltKey) cef_event.modifiers |= KEY_ALT; if (event.modifiers & WebKit::WebKeyboardEvent::MetaKey) cef_event.modifiers |= KEY_META; if (event.modifiers & WebKit::WebKeyboardEvent::IsKeyPad) cef_event.modifiers |= KEY_KEYPAD; cef_event.windows_key_code = event.windowsKeyCode; cef_event.native_key_code = event.nativeKeyCode; cef_event.is_system_key = event.isSystemKey; cef_event.character = event.text[0]; cef_event.unmodified_character = event.unmodifiedText[0]; return true; } } // namespace // CefBrowserHost static methods. // ----------------------------------------------------------------------------- // static bool CefBrowserHost::CreateBrowser(const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { NOTREACHED() << "context not valid"; return false; } // Verify that the settings structure is a valid size. if (settings.size != sizeof(cef_browser_settings_t)) { NOTREACHED() << "invalid CefBrowserSettings structure size"; return false; } // Create the browser on the UI thread. CreateBrowserHelper* helper = new CreateBrowserHelper(windowInfo, client, url, settings); CEF_POST_TASK(CEF_UIT, base::Bind(CreateBrowserWithHelper, helper)); return true; } // static CefRefPtr CefBrowserHost::CreateBrowserSync( const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { NOTREACHED() << "context not valid"; return NULL; } // Verify that the settings structure is a valid size. if (settings.size != sizeof(cef_browser_settings_t)) { NOTREACHED() << "invalid CefBrowserSettings structure size"; return NULL; } // Verify that this method is being called on the UI thread. if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return NULL; } CefRefPtr browser = CefBrowserHostImpl::Create(windowInfo, settings, client, NULL, NULL); if (!url.empty()) browser->LoadURL(CefFrameHostImpl::kMainFrameId, url); return browser.get(); } // CefBrowserHostImpl static methods. // ----------------------------------------------------------------------------- // static CefRefPtr CefBrowserHostImpl::Create( const CefWindowInfo& window_info, const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, CefWindowHandle opener) { CEF_REQUIRE_UIT(); if (web_contents == NULL) { web_contents = content::WebContents::Create( _Context->browser_context(), NULL, MSG_ROUTING_NONE, NULL); } CefRefPtr browser = new CefBrowserHostImpl(window_info, settings, client, web_contents, opener); if (!browser->PlatformCreateWindow()) return NULL; _Context->AddBrowser(browser); if (client.get()) { CefRefPtr handler = client->GetLifeSpanHandler(); if (handler.get()) handler->OnAfterCreated(browser.get()); } return browser; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForHost( const content::RenderViewHost* host) { DCHECK(host); CEF_REQUIRE_UIT(); WebContentsImpl* web_contents = static_cast(host->GetDelegate()); if (web_contents) return static_cast(web_contents->GetDelegate()); return NULL; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForContents( content::WebContents* contents) { DCHECK(contents); CEF_REQUIRE_UIT(); return static_cast(contents->GetDelegate()); } // static CefRefPtr CefBrowserHostImpl::GetBrowserForRequest( net::URLRequest* request) { DCHECK(request); CEF_REQUIRE_IOT(); int render_process_id = -1; int render_view_id = -1; if (!content::ResourceRequestInfo::GetRenderViewForRequest(request, &render_process_id, &render_view_id)) { return NULL; } return GetBrowserByRoutingID(render_process_id, render_view_id); } // static CefRefPtr CefBrowserHostImpl::GetBrowserByRoutingID( int render_process_id, int render_view_id) { if (CEF_CURRENTLY_ON_UIT()) { // Use the non-thread-safe but potentially faster approach. content::RenderViewHost* render_view_host = content::RenderViewHost::FromID(render_process_id, render_view_id); if (!render_view_host) return NULL; return GetBrowserForHost(render_view_host); } else { // Use the thread-safe approach. return _Context->GetBrowserByRoutingID(render_process_id, render_view_id); } } // static CefRefPtr CefBrowserHostImpl::GetBrowserByChildID( int render_process_id) { if (CEF_CURRENTLY_ON_UIT()) { // Use the non-thread-safe but potentially faster approach. content::RenderProcessHost* render_process_host = content::RenderProcessHost::FromID(render_process_id); if (!render_process_host) return NULL; content::RenderProcessHost::RenderWidgetHostsIterator iter( render_process_host->GetRenderWidgetHostsIterator()); if (!iter.IsAtEnd()) { const content::RenderWidgetHost* widget = iter.GetCurrentValue(); // This implementation is based on an assumption that each process hosts a // single renderer. Revisit this implementation if the assumption proves // false. iter.Advance(); DCHECK(iter.IsAtEnd()); if (widget && widget->IsRenderView()) { return GetBrowserForHost(content::RenderViewHost::From( const_cast(widget))); } } return NULL; } else { // Use the thread-safe approach. return _Context->GetBrowserByRoutingID(render_process_id, 0); } } // CefBrowserHost methods. // ----------------------------------------------------------------------------- CefRefPtr CefBrowserHostImpl::GetBrowser() { return this; } void CefBrowserHostImpl::CloseBrowser() { if (CEF_CURRENTLY_ON_UIT()) { PlatformCloseWindow(); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::PlatformCloseWindow, this)); } } void CefBrowserHostImpl::ParentWindowWillClose() { } void CefBrowserHostImpl::SetFocus(bool enable) { if (!enable) return; OnSetFocus(FOCUS_SOURCE_SYSTEM); } CefWindowHandle CefBrowserHostImpl::GetWindowHandle() { return PlatformGetWindowHandle(); } CefWindowHandle CefBrowserHostImpl::GetOpenerWindowHandle() { return opener_; } CefRefPtr CefBrowserHostImpl::GetClient() { return client_; } CefString CefBrowserHostImpl::GetDevToolsURL(bool http_scheme) { base::AutoLock lock_scope(state_lock_); return (http_scheme ? devtools_url_http_ : devtools_url_chrome_); } double CefBrowserHostImpl::GetZoomLevel() { // Verify that this method is being called on the UI thread. if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return 0; } if (web_contents_.get()) return web_contents_->GetZoomLevel(); return 0; } void CefBrowserHostImpl::SetZoomLevel(double zoomLevel) { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get() && web_contents_->GetRenderViewHost()) web_contents_->GetRenderViewHost()->SetZoomLevel(zoomLevel); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::SetZoomLevel, this, zoomLevel)); } } // CefBrowser methods. // ----------------------------------------------------------------------------- CefRefPtr CefBrowserHostImpl::GetHost() { return this; } bool CefBrowserHostImpl::CanGoBack() { base::AutoLock lock_scope(state_lock_); return can_go_back_; } void CefBrowserHostImpl::GoBack() { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get() && web_contents_->GetController().CanGoBack()) web_contents_->GetController().GoBack(); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::GoBack, this)); } } bool CefBrowserHostImpl::CanGoForward() { base::AutoLock lock_scope(state_lock_); return can_go_forward_; } void CefBrowserHostImpl::GoForward() { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get() && web_contents_->GetController().CanGoForward()) web_contents_->GetController().GoForward(); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::GoForward, this)); } } bool CefBrowserHostImpl::IsLoading() { base::AutoLock lock_scope(state_lock_); return is_loading_; } void CefBrowserHostImpl::Reload() { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get()) web_contents_->GetController().Reload(true); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::Reload, this)); } } void CefBrowserHostImpl::ReloadIgnoreCache() { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get()) web_contents_->GetController().ReloadIgnoringCache(true); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::ReloadIgnoreCache, this)); } } void CefBrowserHostImpl::StopLoad() { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get()) web_contents_->Stop(); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::StopLoad, this)); } } int CefBrowserHostImpl::GetIdentifier() { return unique_id(); } bool CefBrowserHostImpl::IsPopup() { return (opener_ != NULL); } bool CefBrowserHostImpl::HasDocument() { base::AutoLock lock_scope(state_lock_); return has_document_; } CefRefPtr CefBrowserHostImpl::GetMainFrame() { return GetFrame(CefFrameHostImpl::kMainFrameId); } CefRefPtr CefBrowserHostImpl::GetFocusedFrame() { return GetFrame(CefFrameHostImpl::kFocusedFrameId); } CefRefPtr CefBrowserHostImpl::GetFrame(int64 identifier) { base::AutoLock lock_scope(state_lock_); if (main_frame_id_ == CefFrameHostImpl::kInvalidFrameId) { // A main frame does not exist yet. Return the placeholder frame that // provides limited functionality. return placeholder_frame_.get(); } if (identifier == CefFrameHostImpl::kMainFrameId) { identifier = main_frame_id_; } else if (identifier == CefFrameHostImpl::kFocusedFrameId) { // Return the main frame if no focused frame is currently identified. if (focused_frame_id_ == CefFrameHostImpl::kInvalidFrameId) identifier = main_frame_id_; else identifier = focused_frame_id_; } if (identifier == CefFrameHostImpl::kInvalidFrameId) return NULL; FrameMap::const_iterator it = frames_.find(identifier); if (it != frames_.end()) return it->second.get(); return NULL; } CefRefPtr CefBrowserHostImpl::GetFrame(const CefString& name) { base::AutoLock lock_scope(state_lock_); FrameMap::const_iterator it = frames_.begin(); for (; it != frames_.end(); ++it) { if (it->second->GetName() == name) return it->second.get(); } return NULL; } size_t CefBrowserHostImpl::GetFrameCount() { base::AutoLock lock_scope(state_lock_); return frames_.size(); } void CefBrowserHostImpl::GetFrameIdentifiers(std::vector& identifiers) { base::AutoLock lock_scope(state_lock_); if (identifiers.size() > 0) identifiers.clear(); FrameMap::const_iterator it = frames_.begin(); for (; it != frames_.end(); ++it) identifiers.push_back(it->first); } void CefBrowserHostImpl::GetFrameNames(std::vector& names) { base::AutoLock lock_scope(state_lock_); if (names.size() > 0) names.clear(); FrameMap::const_iterator it = frames_.begin(); for (; it != frames_.end(); ++it) names.push_back(it->second->GetName()); } bool CefBrowserHostImpl::SendProcessMessage( CefProcessId target_process, CefRefPtr message) { DCHECK_EQ(PID_RENDERER, target_process); DCHECK(message.get()); Cef_Request_Params params; CefProcessMessageImpl* impl = static_cast(message.get()); if (impl->CopyTo(params)) { DCHECK(!params.name.empty()); params.frame_id = -1; params.user_initiated = true; params.request_id = -1; params.expect_response = false; return Send(new CefMsg_Request(routing_id(), params)); } return false; } // CefBrowserHostImpl public methods. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::SetUniqueId(int unique_id) { CEF_REQUIRE_UIT(); unique_id_ = unique_id; Send(new CefMsg_UpdateBrowserWindowId(routing_id(), unique_id, IsPopup())); } void CefBrowserHostImpl::DestroyBrowser() { CEF_REQUIRE_UIT(); if (client_.get()) { CefRefPtr handler = client_->GetLifeSpanHandler(); if (handler.get()) { // Notify the handler that the window is about to be closed. handler->OnBeforeClose(this); } } while (!queued_messages_.empty()) { delete queued_messages_.front(); queued_messages_.pop(); } registrar_.reset(NULL); response_manager_.reset(NULL); content::WebContentsObserver::Observe(NULL); web_contents_.reset(NULL); DetachAllFrames(); request_context_proxy_ = NULL; // Remove the browser from the list maintained by the context. _Context->RemoveBrowser(this); } gfx::NativeView CefBrowserHostImpl::GetContentView() const { CEF_REQUIRE_UIT(); if (!web_contents_.get()) return NULL; return web_contents_->GetNativeView(); } content::WebContents* CefBrowserHostImpl::GetWebContents() const { CEF_REQUIRE_UIT(); return web_contents_.get(); } net::URLRequestContextGetter* CefBrowserHostImpl::GetRequestContext() { CEF_REQUIRE_UIT(); if (!request_context_proxy_) { request_context_proxy_ = new CefURLRequestContextGetterProxy(this, static_cast( _Context->browser_context()->GetRequestContext())); } return request_context_proxy_.get(); } CefRefPtr CefBrowserHostImpl::GetFrameForRequest( net::URLRequest* request) { CEF_REQUIRE_IOT(); const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); if (!info) return NULL; return GetOrCreateFrame(info->GetFrameID(), info->GetParentFrameID(), info->IsMainFrame(), string16(), GURL()); } void CefBrowserHostImpl::Navigate(const CefNavigateParams& params) { // Only known frame ids and kMainFrameId are supported. DCHECK(params.frame_id >= CefFrameHostImpl::kMainFrameId); CefMsg_LoadRequest_Params request; request.url = params.url; if (!request.url.is_valid()) { DLOG(ERROR) << "Invalid URL passed to CefBrowserHostImpl::Navigate: " << params.url; return; } request.method = params.method; request.referrer = params.referrer.url; request.referrer_policy = params.referrer.policy; request.frame_id = params.frame_id; request.first_party_for_cookies = params.first_party_for_cookies; request.headers = params.headers; request.load_flags = params.load_flags; request.upload_data = params.upload_data; Send(new CefMsg_LoadRequest(routing_id(), request)); OnSetFocus(FOCUS_SOURCE_NAVIGATION); } void CefBrowserHostImpl::LoadRequest(int64 frame_id, CefRefPtr request) { CefNavigateParams params(GURL(std::string(request->GetURL())), content::PAGE_TRANSITION_TYPED); params.method = request->GetMethod(); params.frame_id = frame_id; params.first_party_for_cookies = GURL(std::string(request->GetFirstPartyForCookies())); 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(); Navigate(params); } void CefBrowserHostImpl::LoadURL(int64 frame_id, const std::string& url) { if (frame_id == CefFrameHostImpl::kMainFrameId) { // Go through the navigation controller. if (CEF_CURRENTLY_ON_UIT()) { if (web_contents_.get()) { GURL gurl = GURL(url); if (!gurl.is_valid() && !gurl.has_scheme()) { // Try to add "http://" at the beginning std::string new_url = std::string("http://") + url; gurl = GURL(new_url); } if (!gurl.is_valid()) { DLOG(ERROR) << "Invalid URL passed to CefBrowserHostImpl::LoadURL: " << url; return; } // Update the loading URL. OnLoadingURLChange(gurl); web_contents_->GetController().LoadURL( gurl, content::Referrer(), content::PAGE_TRANSITION_TYPED, std::string()); OnSetFocus(FOCUS_SOURCE_NAVIGATION); } } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::LoadURL, this, frame_id, url)); } } else { CefNavigateParams params(GURL(url), content::PAGE_TRANSITION_TYPED); params.frame_id = frame_id; Navigate(params); } } void CefBrowserHostImpl::LoadString(int64 frame_id, const CefString& string, const CefString& url) { // Only known frame ids or kMainFrameId are supported. DCHECK(frame_id >= CefFrameHostImpl::kMainFrameId); Cef_Request_Params params; params.name = "load-string"; params.frame_id = frame_id; params.user_initiated = false; params.request_id = -1; params.expect_response = false; params.arguments.Append(base::Value::CreateStringValue(string.ToString16())); params.arguments.Append(base::Value::CreateStringValue(url.ToString16())); Send(new CefMsg_Request(routing_id(), params)); } void CefBrowserHostImpl::SendCommand( int64 frame_id, const CefString& command, CefRefPtr responseHandler) { // Only known frame ids are supported. DCHECK(frame_id > CefFrameHostImpl::kMainFrameId); DCHECK(!command.empty()); // Execute on the UI thread because CefResponseManager is not thread safe. if (CEF_CURRENTLY_ON_UIT()) { Cef_Request_Params params; params.name = "execute-command"; params.frame_id = frame_id; params.user_initiated = false; if (responseHandler.get()) { params.request_id = response_manager_->RegisterHandler(responseHandler); params.expect_response = true; } else { params.request_id = -1; params.expect_response = false; } params.arguments.Append( base::Value::CreateStringValue(command.ToString16())); Send(new CefMsg_Request(routing_id(), params)); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::SendCommand, this, frame_id, command, responseHandler)); } } void CefBrowserHostImpl::SendCode( int64 frame_id, bool is_javascript, const CefString& code, const CefString& script_url, int script_start_line, CefRefPtr responseHandler) { // Only known frame ids are supported. DCHECK(frame_id >= CefFrameHostImpl::kMainFrameId); DCHECK(!code.empty()); DCHECK_GE(script_start_line, 0); // Execute on the UI thread because CefResponseManager is not thread safe. if (CEF_CURRENTLY_ON_UIT()) { Cef_Request_Params params; params.name = "execute-code"; params.frame_id = frame_id; params.user_initiated = false; if (responseHandler.get()) { params.request_id = response_manager_->RegisterHandler(responseHandler); params.expect_response = true; } else { params.request_id = -1; params.expect_response = false; } params.arguments.Append(base::Value::CreateBooleanValue(is_javascript)); params.arguments.Append(base::Value::CreateStringValue(code.ToString16())); params.arguments.Append( base::Value::CreateStringValue(script_url.ToString16())); params.arguments.Append(base::Value::CreateIntegerValue(script_start_line)); Send(new CefMsg_Request(routing_id(), params)); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::SendCode, this, frame_id, is_javascript, code, script_url, script_start_line, responseHandler)); } } bool CefBrowserHostImpl::ViewText(const std::string& text) { return PlatformViewText(text); } void CefBrowserHostImpl::HandleExternalProtocol(const GURL& url) { if (CEF_CURRENTLY_ON_UIT()) { bool allow_os_execution = false; if (client_.get()) { CefRefPtr handler = client_->GetRequestHandler(); if (handler.get()) handler->OnProtocolExecution(this, url.spec(), allow_os_execution); } if (allow_os_execution) PlatformHandleExternalProtocol(url); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::HandleExternalProtocol, this, url)); } } bool CefBrowserHostImpl::HasIDMatch(int render_process_id, int render_view_id) { base::AutoLock lock_scope(state_lock_); if (render_process_id != render_process_id_) return false; return (render_view_id == 0 || render_view_id == render_view_id_); } GURL CefBrowserHostImpl::GetLoadingURL() { base::AutoLock lock_scope(state_lock_); return loading_url_; } void CefBrowserHostImpl::OnSetFocus(cef_focus_source_t source) { if (CEF_CURRENTLY_ON_UIT()) { // SetFocus() might be called while inside the OnSetFocus() callback. If so, // don't re-enter the callback. if (!is_in_onsetfocus_) { if (client_.get()) { CefRefPtr handler = client_->GetFocusHandler(); if (handler.get()) { is_in_onsetfocus_ = true; bool handled = handler->OnSetFocus(this, source); is_in_onsetfocus_ = false; if (handled) return; } } } if (web_contents_.get()) web_contents_->Focus(); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::OnSetFocus, this, source)); } } // content::WebContentsDelegate methods. // ----------------------------------------------------------------------------- content::WebContents* CefBrowserHostImpl::OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params) { // Start the new navigation. CefNavigateParams nav_params(params.url, params.transition); nav_params.referrer = params.referrer; nav_params.frame_id = params.source_frame_id; nav_params.disposition = params.disposition; nav_params.user_gesture = true; nav_params.override_encoding = params.override_encoding; nav_params.is_renderer_initiated = params.is_renderer_initiated; nav_params.transferred_global_request_id = params.transferred_global_request_id; Navigate(nav_params); return source; } void CefBrowserHostImpl::LoadingStateChanged(content::WebContents* source) { int current_index = web_contents_->GetController().GetCurrentEntryIndex(); int max_index = web_contents_->GetController().GetEntryCount() - 1; bool is_loading, can_go_back, can_go_forward; { base::AutoLock lock_scope(state_lock_); is_loading = is_loading_ = web_contents_->IsLoading(); can_go_back = can_go_back_ = (current_index > 0); can_go_forward = can_go_forward_ = (current_index < max_index); } if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { handler->OnLoadingStateChange(this, is_loading, can_go_back, can_go_forward); } } } void CefBrowserHostImpl::CloseContents(content::WebContents* source) { PlatformCloseWindow(); } void CefBrowserHostImpl::UpdateTargetURL(content::WebContents* source, int32 page_id, const GURL& url) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) handler->OnStatusMessage(this, url.spec()); } } bool CefBrowserHostImpl::AddMessageToConsole(content::WebContents* source, int32 level, const string16& message, int32 line_no, const string16& source_id) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) return handler->OnConsoleMessage(this, message, source_id, line_no); } return false; } bool CefBrowserHostImpl::TakeFocus(content::WebContents* source, bool reverse) { if (client_.get()) { CefRefPtr handler = client_->GetFocusHandler(); if (handler.get()) handler->OnTakeFocus(this, !reverse); } return false; } void CefBrowserHostImpl::WebContentsFocused(content::WebContents* contents) { if (client_.get()) { CefRefPtr handler = client_->GetFocusHandler(); if (handler.get()) handler->OnGotFocus(this); } } bool CefBrowserHostImpl::HandleContextMenu( const content::ContextMenuParams& params) { if (!menu_creator_.get()) menu_creator_.reset(new CefMenuCreator(this)); return menu_creator_->CreateContextMenu(params); } bool CefBrowserHostImpl::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { if (client_.get()) { CefRefPtr handler = client_->GetKeyboardHandler(); if (handler.get()) { CefKeyEvent cef_event; if (!GetCefKeyEvent(event, cef_event)) return false; #if defined(OS_WIN) CefEventHandle os_event = const_cast(&event.os_event); #else CefEventHandle os_event = event.os_event; #endif cef_event.focus_on_editable_field = focus_on_editable_field_; return handler->OnPreKeyEvent(this, cef_event, os_event, is_keyboard_shortcut); } } return false; } void CefBrowserHostImpl::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { // Check to see if event should be ignored. if (event.skip_in_browser) return; if (client_.get()) { CefRefPtr handler = client_->GetKeyboardHandler(); if (handler.get()) { CefKeyEvent cef_event; if (GetCefKeyEvent(event, cef_event)) { #if defined(OS_WIN) CefEventHandle os_event = const_cast(&event.os_event); #else CefEventHandle os_event = event.os_event; #endif cef_event.focus_on_editable_field = focus_on_editable_field_; if (handler->OnKeyEvent(this, cef_event, os_event)) return; } } } PlatformHandleKeyboardEvent(event); } bool CefBrowserHostImpl::ShouldCreateWebContents( content::WebContents* web_contents, int route_id, WindowContainerType window_container_type, const string16& frame_name, const GURL& target_url) { // Start with the current browser window's client and settings. pending_client_ = client_; pending_settings_ = settings_; // TODO(cef): Figure out how to populate these values. // See: http://crbug.com/110510 CefPopupFeatures features; pending_window_info_ = CefWindowInfo(); #if defined(OS_WIN) pending_window_info_.SetAsPopup(NULL, CefString()); #endif #if (defined(OS_WIN) || defined(OS_MACOSX)) // Default to the size from the popup features. if (features.xSet) pending_window_info_.x = features.x; if (features.ySet) pending_window_info_.y = features.y; if (features.widthSet) pending_window_info_.width = features.width; if (features.heightSet) pending_window_info_.height = features.height; #endif if (client_.get()) { CefRefPtr handler = client_->GetLifeSpanHandler(); // Give the handler an opportunity to modify window attributes, handler, // or cancel the window creation. if (handler.get() && handler->OnBeforePopup(this, features, pending_window_info_, target_url.spec(), pending_client_, pending_settings_)) { pending_client_ = NULL; return false; } } return true; } void CefBrowserHostImpl::WebContentsCreated( content::WebContents* source_contents, int64 source_frame_id, const GURL& target_url, content::WebContents* new_contents) { CefWindowHandle opener = NULL; if (source_contents) opener = GetBrowserForContents(source_contents)->GetWindowHandle(); CefRefPtr browser = CefBrowserHostImpl::Create( pending_window_info_, pending_settings_, pending_client_, new_contents, opener); pending_client_ = NULL; } void CefBrowserHostImpl::DidNavigateMainFramePostCommit( content::WebContents* tab) { { base::AutoLock lock_scope(state_lock_); has_document_ = false; } // Allow the new page to set the title again. received_page_title_ = false; } content::JavaScriptDialogCreator* CefBrowserHostImpl::GetJavaScriptDialogCreator() { if (!dialog_creator_.get()) dialog_creator_.reset(new CefJavaScriptDialogCreator(this)); return dialog_creator_.get(); } void CefBrowserHostImpl::RunFileChooser( content::WebContents* tab, const content::FileChooserParams& params) { content::RenderViewHost* render_view_host = tab->GetRenderViewHost(); if (!render_view_host) return; if (params.mode != content::FileChooserParams::Open && params.mode != content::FileChooserParams::OpenMultiple) { NOTREACHED() << "unsupported file chooser mode requested"; return; } std::vector fileList; PlatformRunFileChooser(tab, params, fileList); const int kReadFilePermissions = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | base::PLATFORM_FILE_EXCLUSIVE_READ | base::PLATFORM_FILE_ASYNC; // Convert FilePath list to SelectedFileInfo list. std::vector selected_files; for (size_t i = 0; i < fileList.size(); ++i) selected_files.push_back(ui::SelectedFileInfo(fileList[i], FilePath())); // Notify our RenderViewHost in all cases. render_view_host->FilesSelectedInChooser(selected_files, kReadFilePermissions); } void CefBrowserHostImpl::UpdatePreferredSize(content::WebContents* source, const gfx::Size& pref_size) { PlatformSizeTo(pref_size.width(), pref_size.height()); } void CefBrowserHostImpl::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest* request, const content::MediaResponseCallback& callback) { CEF_CURRENTLY_ON_UIT(); content::MediaStreamDevices devices; for (content::MediaStreamDeviceMap::const_iterator it = request->devices.begin(); it != request->devices.end(); ++it) { devices.push_back(*it->second.begin()); } // TODO(cef): Give the user an opportunity to approve the device list or run // the callback with an empty device list to cancel the request. callback.Run(devices); } // content::WebContentsObserver methods. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::RenderViewCreated( content::RenderViewHost* render_view_host) { SetRenderViewHost(render_view_host); } void CefBrowserHostImpl::RenderViewDeleted( content::RenderViewHost* render_view_host) { registrar_->Remove(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(render_view_host)); } void CefBrowserHostImpl::RenderViewReady() { // Send the queued messages. queue_messages_ = false; while (!queued_messages_.empty()) { Send(queued_messages_.front()); queued_messages_.pop(); } } void CefBrowserHostImpl::RenderViewGone(base::TerminationStatus status) { queue_messages_ = true; cef_termination_status_t ts = TS_ABNORMAL_TERMINATION; if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) ts = TS_PROCESS_WAS_KILLED; else if (status == base::TERMINATION_STATUS_PROCESS_CRASHED) ts = TS_PROCESS_CRASHED; else if (status != base::TERMINATION_STATUS_ABNORMAL_TERMINATION) return; if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) handler->OnRenderProcessTerminated(this, ts); } } void CefBrowserHostImpl::DidCommitProvisionalLoadForFrame( int64 frame_id, bool is_main_frame, const GURL& url, content::PageTransition transition_type, content::RenderViewHost* render_view_host) { CefRefPtr frame = GetOrCreateFrame(frame_id, CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, string16(), url); OnLoadStart(frame, url, transition_type); if (is_main_frame) OnAddressChange(frame, url); } void CefBrowserHostImpl::DidFailProvisionalLoad( int64 frame_id, bool is_main_frame, const GURL& validated_url, int error_code, const string16& error_description, content::RenderViewHost* render_view_host) { CefRefPtr frame = GetOrCreateFrame(frame_id, CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, string16(), validated_url); OnLoadError(frame, validated_url, error_code, error_description); } void CefBrowserHostImpl::DocumentAvailableInMainFrame() { base::AutoLock lock_scope(state_lock_); has_document_ = true; } void CefBrowserHostImpl::DidFinishLoad( int64 frame_id, const GURL& validated_url, bool is_main_frame, content::RenderViewHost* render_view_host) { CefRefPtr frame = GetOrCreateFrame(frame_id, CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, string16(), validated_url); OnLoadEnd(frame, validated_url); } void CefBrowserHostImpl::DidFailLoad( int64 frame_id, const GURL& validated_url, bool is_main_frame, int error_code, const string16& error_description, content::RenderViewHost* render_view_host) { CefRefPtr frame = GetOrCreateFrame(frame_id, CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, string16(), validated_url); OnLoadError(frame, validated_url, error_code, error_description); OnLoadEnd(frame, validated_url); } void CefBrowserHostImpl::PluginCrashed(const FilePath& plugin_path) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) handler->OnPluginCrashed(this, plugin_path.value()); } } bool CefBrowserHostImpl::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CefBrowserHostImpl, message) IPC_MESSAGE_HANDLER(CefHostMsg_FrameIdentified, OnFrameIdentified) IPC_MESSAGE_HANDLER(CefHostMsg_FrameDetached, DetachFrame) IPC_MESSAGE_HANDLER(CefHostMsg_FrameFocusChange, SetFocusedFrame) IPC_MESSAGE_HANDLER(CefHostMsg_LoadingURLChange, OnLoadingURLChange) IPC_MESSAGE_HANDLER(CefHostMsg_Request, OnRequest) IPC_MESSAGE_HANDLER(CefHostMsg_Response, OnResponse) IPC_MESSAGE_HANDLER(CefHostMsg_ResponseAck, OnResponseAck) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } bool CefBrowserHostImpl::Send(IPC::Message* message) { if (CEF_CURRENTLY_ON_UIT()) { if (queue_messages_) { queued_messages_.push(message); return true; } else { return content::WebContentsObserver::Send(message); } } else { CEF_POST_TASK(CEF_UIT, base::Bind(base::IgnoreResult(&CefBrowserHostImpl::Send), this, message)); return true; } } // content::WebContentsObserver::OnMessageReceived() message handlers. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::OnFrameIdentified(int64 frame_id, int64 parent_frame_id, string16 name) { bool is_main_frame = (parent_frame_id == CefFrameHostImpl::kMainFrameId); GetOrCreateFrame(frame_id, parent_frame_id, is_main_frame, name, GURL()); } void CefBrowserHostImpl::OnLoadingURLChange(const GURL& loading_url) { base::AutoLock lock_scope(state_lock_); loading_url_ = loading_url; } void CefBrowserHostImpl::OnRequest(const Cef_Request_Params& params) { bool success = false; std::string response; bool expect_response_ack = false; if (params.user_initiated) { // Give the user a chance to handle the request. if (client_.get()) { CefRefPtr message( new CefProcessMessageImpl(const_cast(¶ms), false, true)); success = client_->OnProcessMessageReceived(this, PID_RENDERER, message.get()); message->Detach(NULL); } } else { // Invalid request. NOTREACHED(); } if (params.expect_response) { DCHECK_GE(params.request_id, 0); // Send a response to the renderer. 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 CefMsg_Response(routing_id(), response_params)); } } void CefBrowserHostImpl::OnResponse(const Cef_Response_Params& params) { response_manager_->RunHandler(params); if (params.expect_response_ack) Send(new CefMsg_ResponseAck(routing_id(), params.request_id)); } void CefBrowserHostImpl::OnResponseAck(int request_id) { response_manager_->RunAckHandler(request_id); } // content::NotificationObserver methods. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED || type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE); if (type == content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) { std::pair* title = content::Details >( details).ptr(); if (received_page_title_) return; if (title->first) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { CefString title_str = title->first->GetTitleForDisplay(""); handler->OnTitleChange(this, title_str); } } received_page_title_ = title->second; } } else if (type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE) { focus_on_editable_field_ = *content::Details(details).ptr(); } } // CefBrowserHostImpl private methods. // ----------------------------------------------------------------------------- CefBrowserHostImpl::CefBrowserHostImpl(const CefWindowInfo& window_info, const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, CefWindowHandle opener) : content::WebContentsObserver(web_contents), window_info_(window_info), settings_(settings), client_(client), opener_(opener), render_process_id_(0), render_view_id_(0), unique_id_(0), received_page_title_(false), is_loading_(false), can_go_back_(false), can_go_forward_(false), has_document_(false), queue_messages_(true), main_frame_id_(CefFrameHostImpl::kInvalidFrameId), focused_frame_id_(CefFrameHostImpl::kInvalidFrameId), is_in_onsetfocus_(false), focus_on_editable_field_(false) { web_contents_.reset(web_contents); web_contents->SetDelegate(this); registrar_.reset(new content::NotificationRegistrar); registrar_->Add(this, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED, content::Source(web_contents)); response_manager_.reset(new CefResponseManager); placeholder_frame_ = new CefFrameHostImpl(this, CefFrameHostImpl::kInvalidFrameId, true); SetRenderViewHost(web_contents->GetRenderViewHost()); } void CefBrowserHostImpl::SetRenderViewHost(content::RenderViewHost* rvh) { { base::AutoLock lock_scope(state_lock_); render_view_id_ = rvh->GetRoutingID(); render_process_id_ = rvh->GetProcess()->GetID(); // Update the DevTools URLs, if any. CefDevToolsDelegate* devtools_delegate = _Context->devtools_delegate(); if (devtools_delegate) { devtools_url_http_ = devtools_delegate->GetDevToolsURL(rvh, true); devtools_url_chrome_ = devtools_delegate->GetDevToolsURL(rvh, false); } } if (!registrar_->IsRegistered( this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(rvh))) { registrar_->Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(rvh)); } } CefRefPtr CefBrowserHostImpl::GetOrCreateFrame( int64 frame_id, int64 parent_frame_id, bool is_main_frame, string16 frame_name, const GURL& frame_url) { DCHECK(frame_id > CefFrameHostImpl::kInvalidFrameId); if (frame_id <= CefFrameHostImpl::kInvalidFrameId) return NULL; CefString url; if (frame_url.is_valid()) url = frame_url.spec(); CefString name; if (!frame_name.empty()) name = frame_name; base::AutoLock lock_scope(state_lock_); if (is_main_frame) main_frame_id_ = frame_id; CefRefPtr frame; // Check if a frame object already exists. FrameMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) frame = it->second.get(); if (!frame.get()) { // Create a new frame object. frame = new CefFrameHostImpl(this, frame_id, is_main_frame); frames_.insert(std::make_pair(frame_id, frame)); } if (!url.empty()) frame->SetURL(url); if (!name.empty()) frame->SetName(name); if (parent_frame_id != CefFrameHostImpl::kUnspecifiedFrameId) frame->SetParentId(parent_frame_id); return frame.get(); } void CefBrowserHostImpl::DetachFrame(int64 frame_id) { base::AutoLock lock_scope(state_lock_); FrameMap::iterator it = frames_.find(frame_id); if (it != frames_.end()) { it->second->Detach(); frames_.erase(it); } if (main_frame_id_ == frame_id) main_frame_id_ = CefFrameHostImpl::kInvalidFrameId; if (focused_frame_id_ == frame_id) focused_frame_id_ = CefFrameHostImpl::kInvalidFrameId; } void CefBrowserHostImpl::DetachAllFrames() { base::AutoLock lock_scope(state_lock_); FrameMap::const_iterator it = frames_.begin(); for (; it != frames_.end(); ++it) it->second->Detach(); frames_.clear(); if (main_frame_id_ != CefFrameHostImpl::kInvalidFrameId) main_frame_id_ = CefFrameHostImpl::kInvalidFrameId; if (focused_frame_id_ != CefFrameHostImpl::kInvalidFrameId) focused_frame_id_ = CefFrameHostImpl::kInvalidFrameId; } void CefBrowserHostImpl::SetFocusedFrame(int64 frame_id) { base::AutoLock lock_scope(state_lock_); if (focused_frame_id_ != CefFrameHostImpl::kInvalidFrameId) { // Unfocus the previously focused frame. FrameMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) it->second->SetFocused(false); } if (frame_id != CefFrameHostImpl::kInvalidFrameId) { // Focus the newly focused frame. FrameMap::iterator it = frames_.find(frame_id); if (it != frames_.end()) { it->second->SetFocused(true); focused_frame_id_ = frame_id; return; } } // No valid frame found. focused_frame_id_ = CefFrameHostImpl::kInvalidFrameId; } void CefBrowserHostImpl::OnAddressChange(CefRefPtr frame, const GURL& url) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { // Notify the handler of an address change. handler->OnAddressChange(this, GetMainFrame(), url.spec()); } } } void CefBrowserHostImpl::OnLoadStart(CefRefPtr frame, const GURL& url, content::PageTransition transition_type) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { // Notify the handler that loading has started. handler->OnLoadStart(this, frame); } } } void CefBrowserHostImpl::OnLoadError(CefRefPtr frame, const GURL& url, int error_code, const string16& error_description) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { // Notify the handler that loading has failed. handler->OnLoadError(this, frame, static_cast(error_code), CefString(error_description), url.spec()); } } } void CefBrowserHostImpl::OnLoadEnd(CefRefPtr frame, const GURL& url) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { // Notify the handler that loading has ended. // TODO(cef): Identify the HTTP status code. handler->OnLoadEnd(this, frame, 200); } } }