// 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_impl.h" #include "libcef/browser/browser_info.h" #include "libcef/browser/browser_info_manager.h" #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/browser_util.h" #include "libcef/browser/content_browser_client.h" #include "libcef/browser/context.h" #include "libcef/browser/devtools_frontend.h" #include "libcef/browser/devtools_manager_delegate.h" #include "libcef/browser/extensions/browser_extensions_util.h" #include "libcef/browser/extensions/extension_background_host.h" #include "libcef/browser/extensions/extension_system.h" #include "libcef/browser/extensions/extension_view_host.h" #include "libcef/browser/extensions/extension_web_contents_observer.h" #include "libcef/browser/image_impl.h" #include "libcef/browser/media_capture_devices_dispatcher.h" #include "libcef/browser/navigate_params.h" #include "libcef/browser/navigation_entry_impl.h" #include "libcef/browser/net/chrome_scheme_handler.h" #include "libcef/browser/net/scheme_handler.h" #include "libcef/browser/osr/osr_util.h" #include "libcef/browser/printing/print_view_manager.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/thread_util.h" #include "libcef/common/cef_messages.h" #include "libcef/common/cef_switches.h" #include "libcef/common/drag_data_impl.h" #include "libcef/common/extensions/extensions_util.h" #include "libcef/common/main_delegate.h" #include "libcef/common/process_message_impl.h" #include "libcef/common/request_impl.h" #include "libcef/common/values_impl.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "chrome/browser/spellchecker/spellcheck_factory.h" #include "chrome/browser/spellchecker/spellcheck_service.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "components/zoom/zoom_controller.h" #include "content/browser/gpu/compositor_util.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/view_messages.h" #include "content/public/browser/desktop_media_id.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/download_request_utils.h" #include "content/public/browser/host_zoom_map.h" #include "content/public/browser/keyboard_event_processing_result.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/navigation_handle.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/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/resource_request_info.h" #include "content/public/common/favicon_url.h" #include "extensions/browser/process_manager.h" #include "net/base/net_errors.h" #include "third_party/blink/public/web/web_find_options.h" #include "ui/events/base_event_utils.h" #if defined(OS_MACOSX) #include "components/spellcheck/browser/spellcheck_platform.h" #endif using content::KeyboardEventProcessingResult; namespace { const int kUnspecifiedFrameTreeNodeId = -3; const int kMainFrameTreeNodeId = -2; const int kUnusedFrameTreeNodeId = -1; // Associates a CefBrowserHostImpl instance with a WebContents. This object will // be deleted automatically when the WebContents is destroyed. class WebContentsUserDataAdapter : public base::SupportsUserData::Data { public: static void Register(CefBrowserHostImpl* browser) { new WebContentsUserDataAdapter(browser); } static CefBrowserHostImpl* Get(const content::WebContents* web_contents) { WebContentsUserDataAdapter* adapter = static_cast( web_contents->GetUserData(UserDataKey())); if (adapter) return adapter->browser_; return nullptr; } private: WebContentsUserDataAdapter(CefBrowserHostImpl* browser) : browser_(browser) { browser->web_contents()->SetUserData(UserDataKey(), base::WrapUnique(this)); } static void* UserDataKey() { // We just need a unique constant. Use the address of a static that // COMDAT folding won't touch in an optimizing linker. static int data_key = 0; return reinterpret_cast(&data_key); } CefBrowserHostImpl* browser_; // Not owned. }; class CreateBrowserHelper { public: CreateBrowserHelper(const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings, CefRefPtr request_context) : window_info_(windowInfo), client_(client), url_(url), settings_(settings), request_context_(request_context) {} CefWindowInfo window_info_; CefRefPtr client_; CefString url_; CefBrowserSettings settings_; CefRefPtr request_context_; }; void CreateBrowserWithHelper(CreateBrowserHelper* helper) { CefBrowserHost::CreateBrowserSync(helper->window_info_, helper->client_, helper->url_, helper->settings_, helper->request_context_); delete helper; } class ShowDevToolsHelper { public: ShowDevToolsHelper(CefRefPtr browser, const CefWindowInfo& windowInfo, CefRefPtr client, const CefBrowserSettings& settings, const CefPoint& inspect_element_at) : browser_(browser), window_info_(windowInfo), client_(client), settings_(settings), inspect_element_at_(inspect_element_at) {} CefRefPtr browser_; CefWindowInfo window_info_; CefRefPtr client_; CefBrowserSettings settings_; CefPoint inspect_element_at_; }; void ShowDevToolsWithHelper(ShowDevToolsHelper* helper) { helper->browser_->ShowDevTools(helper->window_info_, helper->client_, helper->settings_, helper->inspect_element_at_); delete helper; } // Callback from CefBrowserHostImpl::DownloadImage. void OnDownloadImage(uint32 max_image_size, CefRefPtr callback, int id, int http_status_code, const GURL& image_url, const std::vector& bitmaps, const std::vector& sizes) { CEF_REQUIRE_UIT(); CefRefPtr image_impl; if (!bitmaps.empty()) { image_impl = new CefImageImpl(); image_impl->AddBitmaps(max_image_size, bitmaps); } callback->OnDownloadImageFinished(image_url.spec(), http_status_code, image_impl.get()); } } // namespace // CefBrowserHost static methods. // ----------------------------------------------------------------------------- // static bool CefBrowserHost::CreateBrowser( const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings, CefRefPtr request_context) { // 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; } // Verify windowless rendering requirements. if (windowInfo.windowless_rendering_enabled && !client->GetRenderHandler().get()) { NOTREACHED() << "CefRenderHandler implementation is required"; return false; } if (windowInfo.windowless_rendering_enabled && !CefContext::Get()->settings().windowless_rendering_enabled) { LOG(ERROR) << "Creating a windowless browser without setting " "CefSettings.windowless_rendering_enabled may result in " "reduced performance or runtime errors."; } // Create the browser on the UI thread. CreateBrowserHelper* helper = new CreateBrowserHelper( windowInfo, client, url, settings, request_context); CEF_POST_TASK(CEF_UIT, base::BindOnce(CreateBrowserWithHelper, helper)); return true; } // static CefRefPtr CefBrowserHost::CreateBrowserSync( const CefWindowInfo& windowInfo, CefRefPtr client, const CefString& url, const CefBrowserSettings& settings, CefRefPtr request_context) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { NOTREACHED() << "context not valid"; return nullptr; } // Verify that the settings structure is a valid size. if (settings.size != sizeof(cef_browser_settings_t)) { NOTREACHED() << "invalid CefBrowserSettings structure size"; return nullptr; } // Verify that this method is being called on the UI thread. if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return nullptr; } // Verify windowless rendering requirements. if (windowInfo.windowless_rendering_enabled && !client->GetRenderHandler().get()) { NOTREACHED() << "CefRenderHandler implementation is required"; return nullptr; } CefBrowserHostImpl::CreateParams create_params; create_params.window_info.reset(new CefWindowInfo(windowInfo)); create_params.client = client; create_params.url = GURL(url.ToString()); if (!url.empty() && !create_params.url.is_valid() && !create_params.url.has_scheme()) { std::string new_url = std::string("http://") + url.ToString(); create_params.url = GURL(new_url); } create_params.settings = settings; create_params.request_context = request_context; CefRefPtr browser = CefBrowserHostImpl::Create(create_params); return browser.get(); } // CefBrowserHostImpl static methods. // ----------------------------------------------------------------------------- // static CefRefPtr CefBrowserHostImpl::Create( CreateParams& create_params) { std::unique_ptr platform_delegate = CefBrowserPlatformDelegate::Create(create_params); CHECK(platform_delegate); const bool is_devtools_popup = !!create_params.devtools_opener; scoped_refptr info = CefBrowserInfoManager::GetInstance()->CreateBrowserInfo( is_devtools_popup, platform_delegate->IsWindowless()); // Get or create the request context and browser context. CefRefPtr request_context_impl = CefRequestContextImpl::GetOrCreateForRequestContext( create_params.request_context); DCHECK(request_context_impl); CefBrowserContext* browser_context = request_context_impl->GetBrowserContext(); DCHECK(browser_context); // A StoragePartitionImplMap must already exist for the BrowserContext. See // additional comments in CefBrowserContextImpl::Initialize(). DCHECK(browser_context->GetUserData( content::BrowserContext::GetStoragePartitionMapUserDataKey())); if (!create_params.request_context) { // Using the global request context. create_params.request_context = request_context_impl.get(); } CefRefPtr cef_extension; scoped_refptr site_instance; if (extensions::ExtensionsEnabled() && !create_params.url.is_empty()) { if (!create_params.extension) { // We might be loading an extension app view where the extension URL is // provided by the client. create_params.extension = extensions::GetExtensionForUrl(browser_context, create_params.url); } if (create_params.extension) { cef_extension = browser_context->extension_system()->GetExtension( create_params.extension->id()); DCHECK(cef_extension); if (create_params.extension_host_type == extensions::VIEW_TYPE_INVALID) { // Default to dialog behavior. create_params.extension_host_type = extensions::VIEW_TYPE_EXTENSION_DIALOG; } // Extension resources will fail to load if we don't use a SiteInstance // associated with the extension. // (CefContentBrowserClient::SiteInstanceGotProcess won't find the // extension to register with InfoMap, and AllowExtensionResourceLoad in // ExtensionProtocolHandler::MaybeCreateJob will return false resulting in // ERR_BLOCKED_BY_CLIENT). site_instance = extensions::ProcessManager::Get(browser_context) ->GetSiteInstanceForURL(create_params.url); DCHECK(site_instance); } } content::WebContents::CreateParams wc_create_params(browser_context, site_instance); if (platform_delegate->IsWindowless()) { // Create the OSR view for the WebContents. platform_delegate->CreateViewForWebContents( &wc_create_params.view, &wc_create_params.delegate_view); } std::unique_ptr web_contents = content::WebContents::Create(wc_create_params); DCHECK(web_contents); CefRefPtr browser = CreateInternal( create_params.settings, create_params.client, web_contents.release(), true, info, create_params.devtools_opener, is_devtools_popup, create_params.request_context, std::move(platform_delegate), cef_extension); if (!browser) return nullptr; if (create_params.extension) { browser->CreateExtensionHost(create_params.extension, browser_context, browser->web_contents(), create_params.url, create_params.extension_host_type); } else if (!create_params.url.is_empty()) { browser->LoadURL(CefFrameHostImpl::kMainFrameId, create_params.url.spec(), content::Referrer(), ui::PAGE_TRANSITION_TYPED, std::string()); } return browser.get(); } // static CefRefPtr CefBrowserHostImpl::CreateInternal( const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, bool own_web_contents, scoped_refptr browser_info, CefRefPtr opener, bool is_devtools_popup, CefRefPtr request_context, std::unique_ptr platform_delegate, CefRefPtr extension) { CEF_REQUIRE_UIT(); DCHECK(web_contents); DCHECK(browser_info); DCHECK(request_context); DCHECK(platform_delegate); // If |opener| is non-NULL it must be a popup window. DCHECK(!opener.get() || browser_info->is_popup()); if (opener) { if (!opener->platform_delegate_) { // The opener window is being destroyed. Cancel the popup. return nullptr; } // Give the opener browser's platform delegate an opportunity to modify the // new browser's platform delegate. opener->platform_delegate_->PopupWebContentsCreated( settings, client, web_contents, platform_delegate.get(), is_devtools_popup); } platform_delegate->WebContentsCreated(web_contents); CefRefPtr browser = new CefBrowserHostImpl( settings, client, web_contents, browser_info, opener, request_context, std::move(platform_delegate), extension); if (own_web_contents) browser->set_owned_web_contents(web_contents); if (!browser->CreateHostWindow()) return nullptr; // Notify that the browser has been created. These must be delivered in the // expected order. // 1. Notify the browser's LifeSpanHandler. This must always be the first // notification for the browser. if (client.get()) { CefRefPtr handler = client->GetLifeSpanHandler(); if (handler.get()) handler->OnAfterCreated(browser.get()); } // 2. Notify the platform delegate. With Views this will result in a call to // CefBrowserViewDelegate::OnBrowserCreated(). browser->platform_delegate_->NotifyBrowserCreated(); if (opener && opener->platform_delegate_) { // 3. Notify the opener browser's platform delegate. With Views this will // result in a call to CefBrowserViewDelegate::OnPopupBrowserViewCreated(). opener->platform_delegate_->PopupBrowserCreated(browser.get(), is_devtools_popup); } return browser; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForHost( const content::RenderViewHost* host) { DCHECK(host); CEF_REQUIRE_UIT(); content::WebContents* web_contents = content::WebContents::FromRenderViewHost( const_cast(host)); if (web_contents) return GetBrowserForContents(web_contents); return nullptr; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForHost( const content::RenderFrameHost* host) { DCHECK(host); CEF_REQUIRE_UIT(); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost( const_cast(host)); if (web_contents) return GetBrowserForContents(web_contents); return nullptr; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForContents( const content::WebContents* contents) { DCHECK(contents); CEF_REQUIRE_UIT(); return WebContentsUserDataAdapter::Get(contents); } // static CefRefPtr CefBrowserHostImpl::GetBrowserForRequest( const net::URLRequest* request) { DCHECK(request); CEF_REQUIRE_IOT(); // When navigating the main frame a new (pre-commit) URLRequest will be // created before the RenderFrameHost. Consequently we can't rely on // ResourceRequestInfo::GetRenderFrameForRequest returning a valid frame // ID. See https://crbug.com/776884 for background. int render_process_id = -1; int render_frame_id = MSG_ROUTING_NONE; if (content::ResourceRequestInfo::GetRenderFrameForRequest( request, &render_process_id, &render_frame_id) && render_process_id >= 0 && render_frame_id >= 0) { return GetBrowserForFrame(render_process_id, render_frame_id); } const content::ResourceRequestInfo* request_info = content::ResourceRequestInfo::ForRequest(request); if (request_info) return GetBrowserForFrameTreeNode(request_info->GetFrameTreeNodeId()); return nullptr; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForFrameTreeNode( int frame_tree_node_id) { // Use the thread-safe approach. scoped_refptr info = CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode( frame_tree_node_id); if (info.get()) { CefRefPtr browser = info->browser(); if (!browser.get()) { LOG(WARNING) << "Found browser id " << info->browser_id() << " but no browser object matching frame tree node id " << frame_tree_node_id; } return browser; } return nullptr; } // static CefRefPtr CefBrowserHostImpl::GetBrowserForFrame( int render_process_id, int render_routing_id) { if (render_process_id == -1 || render_routing_id == MSG_ROUTING_NONE) return nullptr; if (CEF_CURRENTLY_ON_UIT()) { // Use the non-thread-safe but potentially faster approach. content::RenderFrameHost* render_frame_host = content::RenderFrameHost::FromID(render_process_id, render_routing_id); if (!render_frame_host) return nullptr; return GetBrowserForHost(render_frame_host); } else { // Use the thread-safe approach. bool is_guest_view = false; scoped_refptr info = CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrame( render_process_id, render_routing_id, &is_guest_view); if (info.get() && !is_guest_view) { CefRefPtr browser = info->browser(); if (!browser.get()) { LOG(WARNING) << "Found browser id " << info->browser_id() << " but no browser object matching frame process id " << render_process_id << " and routing id " << render_routing_id; } return browser; } return nullptr; } } // CefBrowserHostImpl methods. // ----------------------------------------------------------------------------- // WebContentsObserver that will be notified when the frontend WebContents is // destroyed so that the inspected browser can clear its DevTools references. class CefBrowserHostImpl::DevToolsWebContentsObserver : public content::WebContentsObserver { public: DevToolsWebContentsObserver(CefBrowserHostImpl* browser, content::WebContents* frontend_web_contents) : WebContentsObserver(frontend_web_contents), browser_(browser) {} // WebContentsObserver methods: void WebContentsDestroyed() override { browser_->OnDevToolsWebContentsDestroyed(); } private: CefBrowserHostImpl* browser_; DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver); }; CefBrowserHostImpl::~CefBrowserHostImpl() {} CefRefPtr CefBrowserHostImpl::GetBrowser() { return this; } void CefBrowserHostImpl::CloseBrowser(bool force_close) { if (CEF_CURRENTLY_ON_UIT()) { // Exit early if a close attempt is already pending and this method is // called again from somewhere other than WindowDestroyed(). if (destruction_state_ >= DESTRUCTION_STATE_PENDING && (IsWindowless() || !window_destroyed_)) { if (force_close && destruction_state_ == DESTRUCTION_STATE_PENDING) { // Upgrade the destruction state. destruction_state_ = DESTRUCTION_STATE_ACCEPTED; } return; } if (destruction_state_ < DESTRUCTION_STATE_ACCEPTED) { destruction_state_ = (force_close ? DESTRUCTION_STATE_ACCEPTED : DESTRUCTION_STATE_PENDING); } content::WebContents* contents = web_contents(); if (contents && contents->NeedToFireBeforeUnload()) { // Will result in a call to BeforeUnloadFired() and, if the close isn't // canceled, CloseContents(). contents->DispatchBeforeUnload(); } else { CloseContents(contents); } } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::CloseBrowser, this, force_close)); } } bool CefBrowserHostImpl::TryCloseBrowser() { if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return false; } // Protect against multiple requests to close while the close is pending. if (destruction_state_ <= DESTRUCTION_STATE_PENDING) { if (destruction_state_ == DESTRUCTION_STATE_NONE) { // Request that the browser close. CloseBrowser(false); } // Cancel the close. return false; } // Allow the close. return true; } void CefBrowserHostImpl::SetFocus(bool focus) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetFocus, this, focus)); return; } if (focus) OnSetFocus(FOCUS_SOURCE_SYSTEM); else if (platform_delegate_) platform_delegate_->SendFocusEvent(false); } CefWindowHandle CefBrowserHostImpl::GetWindowHandle() { if (IsViewsHosted() && CEF_CURRENTLY_ON_UIT()) { // Always return the most up-to-date window handle for a views-hosted // browser since it may change if the view is re-parented. if (platform_delegate_) return platform_delegate_->GetHostWindowHandle(); } return host_window_handle_; } CefWindowHandle CefBrowserHostImpl::GetOpenerWindowHandle() { return opener_; } bool CefBrowserHostImpl::HasView() { return IsViewsHosted(); } CefRefPtr CefBrowserHostImpl::GetClient() { return client_; } CefRefPtr CefBrowserHostImpl::GetRequestContext() { return request_context_; } 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()) return content::HostZoomMap::GetZoomLevel(web_contents()); return 0; } void CefBrowserHostImpl::SetZoomLevel(double zoomLevel) { if (CEF_CURRENTLY_ON_UIT()) { if (web_contents()) content::HostZoomMap::SetZoomLevel(web_contents(), zoomLevel); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetZoomLevel, this, zoomLevel)); } } void CefBrowserHostImpl::RunFileDialog( FileDialogMode mode, const CefString& title, const CefString& default_file_path, const std::vector& accept_filters, int selected_accept_filter, CefRefPtr callback) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::RunFileDialog, this, mode, title, default_file_path, accept_filters, selected_accept_filter, callback)); return; } EnsureFileDialogManager(); file_dialog_manager_->RunFileDialog(mode, title, default_file_path, accept_filters, selected_accept_filter, callback); } void CefBrowserHostImpl::StartDownload(const CefString& url) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StartDownload, this, url)); return; } GURL gurl = GURL(url.ToString()); if (gurl.is_empty() || !gurl.is_valid()) return; if (!web_contents()) return; CefBrowserContext* context = static_cast(web_contents()->GetBrowserContext()); if (!context) return; content::DownloadManager* manager = content::BrowserContext::GetDownloadManager(context); if (!manager) return; std::unique_ptr params( content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( web_contents(), gurl, NO_TRAFFIC_ANNOTATION_YET)); manager->DownloadUrl(std::move(params)); } void CefBrowserHostImpl::DownloadImage( const CefString& image_url, bool is_favicon, uint32 max_image_size, bool bypass_cache, CefRefPtr callback) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DownloadImage, this, image_url, is_favicon, max_image_size, bypass_cache, callback)); return; } if (!callback) return; GURL gurl = GURL(image_url.ToString()); if (gurl.is_empty() || !gurl.is_valid()) return; if (!web_contents()) return; web_contents()->DownloadImage( gurl, is_favicon, max_image_size * gfx::ImageSkia::GetMaxSupportedScale(), bypass_cache, base::BindOnce(OnDownloadImage, max_image_size, callback)); } void CefBrowserHostImpl::Print() { if (CEF_CURRENTLY_ON_UIT()) { content::WebContents* actionable_contents = GetActionableWebContents(); if (!actionable_contents) return; printing::CefPrintViewManager::FromWebContents(actionable_contents) ->PrintNow(actionable_contents->GetRenderViewHost()->GetMainFrame()); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Print, this)); } } void CefBrowserHostImpl::PrintToPDF(const CefString& path, const CefPdfPrintSettings& settings, CefRefPtr callback) { if (CEF_CURRENTLY_ON_UIT()) { content::WebContents* actionable_contents = GetActionableWebContents(); if (!actionable_contents) return; printing::CefPrintViewManager::PdfPrintCallback pdf_callback; if (callback.get()) { pdf_callback = base::Bind(&CefPdfPrintCallback::OnPdfPrintFinished, callback.get(), path); } printing::CefPrintViewManager::FromWebContents(actionable_contents) ->PrintToPDF(actionable_contents->GetMainFrame(), base::FilePath(path), settings, pdf_callback); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::PrintToPDF, this, path, settings, callback)); } } void CefBrowserHostImpl::Find(int identifier, const CefString& searchText, bool forward, bool matchCase, bool findNext) { if (CEF_CURRENTLY_ON_UIT()) { if (!web_contents()) return; // Every find request must have a unique ID and these IDs must strictly // increase so that newer requests always have greater IDs than older // requests. if (identifier <= find_request_id_counter_) identifier = ++find_request_id_counter_; else find_request_id_counter_ = identifier; blink::WebFindOptions options; options.forward = forward; options.match_case = matchCase; options.find_next = findNext; web_contents()->Find(identifier, searchText, options); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Find, this, identifier, searchText, forward, matchCase, findNext)); } } void CefBrowserHostImpl::StopFinding(bool clearSelection) { if (CEF_CURRENTLY_ON_UIT()) { if (!web_contents()) return; content::StopFindAction action = clearSelection ? content::STOP_FIND_ACTION_CLEAR_SELECTION : content::STOP_FIND_ACTION_KEEP_SELECTION; web_contents()->StopFinding(action); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StopFinding, this, clearSelection)); } } void CefBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo, CefRefPtr client, const CefBrowserSettings& settings, const CefPoint& inspect_element_at) { if (CEF_CURRENTLY_ON_UIT()) { if (!web_contents()) return; if (devtools_frontend_) { if (!inspect_element_at.IsEmpty()) { devtools_frontend_->InspectElementAt(inspect_element_at.x, inspect_element_at.y); } devtools_frontend_->Focus(); return; } devtools_frontend_ = CefDevToolsFrontend::Show( this, windowInfo, client, settings, inspect_element_at); devtools_observer_.reset(new DevToolsWebContentsObserver( this, devtools_frontend_->frontend_browser()->web_contents())); } else { ShowDevToolsHelper* helper = new ShowDevToolsHelper( this, windowInfo, client, settings, inspect_element_at); CEF_POST_TASK(CEF_UIT, base::BindOnce(ShowDevToolsWithHelper, helper)); } } void CefBrowserHostImpl::CloseDevTools() { if (CEF_CURRENTLY_ON_UIT()) { if (!devtools_frontend_) return; devtools_frontend_->Close(); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::CloseDevTools, this)); } } bool CefBrowserHostImpl::HasDevTools() { if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return false; } return (devtools_frontend_ != nullptr); } void CefBrowserHostImpl::GetNavigationEntries( CefRefPtr visitor, bool current_only) { DCHECK(visitor.get()); if (!visitor.get()) return; if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::GetNavigationEntries, this, visitor, current_only)); return; } if (!web_contents()) return; const content::NavigationController& controller = web_contents()->GetController(); const int total = controller.GetEntryCount(); const int current = controller.GetCurrentEntryIndex(); if (current_only) { // Visit only the current entry. CefRefPtr entry = new CefNavigationEntryImpl(controller.GetEntryAtIndex(current)); visitor->Visit(entry.get(), true, current, total); entry->Detach(NULL); } else { // Visit all entries. bool cont = true; for (int i = 0; i < total && cont; ++i) { CefRefPtr entry = new CefNavigationEntryImpl(controller.GetEntryAtIndex(i)); cont = visitor->Visit(entry.get(), (i == current), i, total); entry->Detach(NULL); } } } CefRefPtr CefBrowserHostImpl::GetVisibleNavigationEntry() { if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return nullptr; } content::NavigationEntry* entry = nullptr; if (web_contents()) entry = web_contents()->GetController().GetVisibleEntry(); if (!entry) return nullptr; return new CefNavigationEntryImpl(entry); } void CefBrowserHostImpl::SetAccessibilityState( cef_state_t accessibility_state) { // Do nothing if state is set to default. It'll be disabled by default and // controlled by the commmand-line flags "force-renderer-accessibility" and // "disable-renderer-accessibility". if (accessibility_state == STATE_DEFAULT) return; if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetAccessibilityState, this, accessibility_state)); return; } content::WebContentsImpl* web_contents_impl = static_cast(web_contents()); if (!web_contents_impl) return; ui::AXMode accMode; // In windowless mode set accessibility to TreeOnly mode. Else native // accessibility APIs, specific to each platform, are also created. if (accessibility_state == STATE_ENABLED) { accMode = IsWindowless() ? ui::kAXModeWebContentsOnly : ui::kAXModeComplete; } web_contents_impl->SetAccessibilityMode(accMode); } void CefBrowserHostImpl::SetAutoResizeEnabled(bool enabled, const CefSize& min_size, const CefSize& max_size) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetAutoResizeEnabled, this, enabled, min_size, max_size)); return; } if (enabled == auto_resize_enabled_) return; auto_resize_enabled_ = enabled; if (enabled) { auto_resize_min_ = gfx::Size(min_size.width, min_size.height); auto_resize_max_ = gfx::Size(max_size.width, max_size.height); } else { auto_resize_min_ = auto_resize_max_ = gfx::Size(); } ConfigureAutoResize(); } CefRefPtr CefBrowserHostImpl::GetExtension() { return extension_; } bool CefBrowserHostImpl::IsBackgroundHost() { return is_background_host_; } void CefBrowserHostImpl::SetMouseCursorChangeDisabled(bool disabled) { base::AutoLock lock_scope(state_lock_); mouse_cursor_change_disabled_ = disabled; } bool CefBrowserHostImpl::IsMouseCursorChangeDisabled() { base::AutoLock lock_scope(state_lock_); return mouse_cursor_change_disabled_; } bool CefBrowserHostImpl::IsWindowRenderingDisabled() { return IsWindowless(); } void CefBrowserHostImpl::ReplaceMisspelling(const CefString& word) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ReplaceMisspelling, this, word)); return; } if (web_contents()) web_contents()->ReplaceMisspelling(word); } void CefBrowserHostImpl::AddWordToDictionary(const CefString& word) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::AddWordToDictionary, this, word)); return; } if (!web_contents()) return; content::BrowserContext* browser_context = web_contents()->GetBrowserContext(); if (browser_context) { SpellcheckService* spellcheck = SpellcheckServiceFactory::GetForContext(browser_context); if (spellcheck) spellcheck->GetCustomDictionary()->AddWord(word); } #if defined(OS_MACOSX) spellcheck_platform::AddWord(word); #endif } void CefBrowserHostImpl::WasResized() { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::WasResized, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->SynchronizeVisualProperties(); } void CefBrowserHostImpl::WasHidden(bool hidden) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHost::WasHidden, this, hidden)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->WasHidden(hidden); } void CefBrowserHostImpl::NotifyScreenInfoChanged() { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::NotifyScreenInfoChanged, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->NotifyScreenInfoChanged(); } void CefBrowserHostImpl::Invalidate(PaintElementType type) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Invalidate, this, type)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->Invalidate(type); } void CefBrowserHostImpl::SendKeyEvent(const CefKeyEvent& event) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendKeyEvent, this, event)); return; } if (!web_contents() || !platform_delegate_) return; content::NativeWebKeyboardEvent web_event(blink::WebInputEvent::kUndefined, blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow()); platform_delegate_->TranslateKeyEvent(web_event, event); platform_delegate_->SendKeyEvent(web_event); } void CefBrowserHostImpl::SendMouseClickEvent(const CefMouseEvent& event, MouseButtonType type, bool mouseUp, int clickCount) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendMouseClickEvent, this, event, type, mouseUp, clickCount)); return; } if (!web_contents() || !platform_delegate_) return; blink::WebMouseEvent web_event; platform_delegate_->TranslateClickEvent(web_event, event, type, mouseUp, clickCount); platform_delegate_->SendMouseEvent(web_event); } void CefBrowserHostImpl::SendMouseMoveEvent(const CefMouseEvent& event, bool mouseLeave) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendMouseMoveEvent, this, event, mouseLeave)); return; } if (!web_contents() || !platform_delegate_) return; blink::WebMouseEvent web_event; platform_delegate_->TranslateMoveEvent(web_event, event, mouseLeave); platform_delegate_->SendMouseEvent(web_event); } void CefBrowserHostImpl::SendMouseWheelEvent(const CefMouseEvent& event, int deltaX, int deltaY) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendMouseWheelEvent, this, event, deltaX, deltaY)); return; } if (!web_contents() || !platform_delegate_) return; blink::WebMouseWheelEvent web_event; platform_delegate_->TranslateWheelEvent(web_event, event, deltaX, deltaY); platform_delegate_->SendMouseWheelEvent(web_event); } void CefBrowserHostImpl::SendFocusEvent(bool setFocus) { SetFocus(setFocus); } void CefBrowserHostImpl::SendCaptureLostEvent() { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendCaptureLostEvent, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->SendCaptureLostEvent(); } void CefBrowserHostImpl::NotifyMoveOrResizeStarted() { #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX)) if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::NotifyMoveOrResizeStarted, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->NotifyMoveOrResizeStarted(); #endif } int CefBrowserHostImpl::GetWindowlessFrameRate() { // Verify that this method is being called on the UI thread. if (!CEF_CURRENTLY_ON_UIT()) { NOTREACHED() << "called on invalid thread"; return 0; } return osr_util::ClampFrameRate(settings_.windowless_frame_rate); } void CefBrowserHostImpl::SetWindowlessFrameRate(int frame_rate) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SetWindowlessFrameRate, this, frame_rate)); return; } settings_.windowless_frame_rate = frame_rate; if (platform_delegate_) platform_delegate_->SetWindowlessFrameRate(frame_rate); } // 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 (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::GoBack, this)); return; } if (web_contents() && web_contents()->GetController().CanGoBack()) web_contents()->GetController().GoBack(); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&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 (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::GoForward, this)); return; } if (web_contents() && web_contents()->GetController().CanGoForward()) web_contents()->GetController().GoForward(); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::GoForward, this)); } } bool CefBrowserHostImpl::IsLoading() { base::AutoLock lock_scope(state_lock_); return is_loading_; } void CefBrowserHostImpl::Reload() { if (CEF_CURRENTLY_ON_UIT()) { if (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::Reload, this)); return; } if (web_contents()) web_contents()->GetController().Reload(content::ReloadType::NORMAL, true); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::Reload, this)); } } void CefBrowserHostImpl::ReloadIgnoreCache() { if (CEF_CURRENTLY_ON_UIT()) { if (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::ReloadIgnoreCache, this)); return; } if (web_contents()) { web_contents()->GetController().Reload( content::ReloadType::BYPASSING_CACHE, true); } } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ReloadIgnoreCache, this)); } } void CefBrowserHostImpl::StopLoad() { if (CEF_CURRENTLY_ON_UIT()) { if (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::StopLoad, this)); return; } if (web_contents()) web_contents()->Stop(); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::StopLoad, this)); } } int CefBrowserHostImpl::GetIdentifier() { return browser_id(); } bool CefBrowserHostImpl::IsSame(CefRefPtr that) { CefBrowserHostImpl* impl = static_cast(that.get()); return (impl == this); } bool CefBrowserHostImpl::IsPopup() { return browser_info_->is_popup(); } 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 GetOrCreatePendingFrame(kMainFrameTreeNodeId, CefFrameHostImpl::kInvalidFrameId, nullptr) .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 nullptr; FrameIdMap::const_iterator it = frames_.find(identifier); if (it != frames_.end()) return it->second.get(); return nullptr; } CefRefPtr CefBrowserHostImpl::GetFrame(const CefString& name) { base::AutoLock lock_scope(state_lock_); FrameIdMap::const_iterator it = frames_.begin(); for (; it != frames_.end(); ++it) { if (it->second->GetName() == name) return it->second.get(); } return nullptr; } 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(); FrameIdMap::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(); FrameIdMap::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)) return false; 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(MSG_ROUTING_NONE, params)); } // CefBrowserHostImpl public methods. // ----------------------------------------------------------------------------- bool CefBrowserHostImpl::IsWindowless() const { return is_windowless_; } bool CefBrowserHostImpl::IsViewsHosted() const { return is_views_hosted_; } void CefBrowserHostImpl::WindowDestroyed() { CEF_REQUIRE_UIT(); DCHECK(!window_destroyed_); window_destroyed_ = true; CloseBrowser(true); } void CefBrowserHostImpl::DestroyBrowser() { CEF_REQUIRE_UIT(); destruction_state_ = DESTRUCTION_STATE_COMPLETED; // Notify that this browser has been destroyed. These must be delivered in the // expected order. // 1. Notify the platform delegate. With Views this will result in a call to // CefBrowserViewDelegate::OnBrowserDestroyed(). platform_delegate_->NotifyBrowserDestroyed(); // 2. Notify the browser's LifeSpanHandler. This must always be the last // notification for this browser. if (client_.get()) { CefRefPtr handler = client_->GetLifeSpanHandler(); if (handler.get()) { // Notify the handler that the window is about to be closed. handler->OnBeforeClose(this); } } // Destroy any platform constructs first. if (file_dialog_manager_.get()) file_dialog_manager_->Destroy(); if (javascript_dialog_manager_.get()) javascript_dialog_manager_->Destroy(); if (menu_manager_.get()) menu_manager_->Destroy(); DestroyExtensionHost(); // Notify any observers that may have state associated with this browser. for (auto& observer : observers_) observer.OnBrowserDestroyed(this); // Disassociate the platform delegate from this browser. platform_delegate_->BrowserDestroyed(this); while (!queued_messages_.empty()) { delete queued_messages_.front(); queued_messages_.pop(); } registrar_.reset(NULL); response_manager_.reset(NULL); content::WebContentsObserver::Observe(NULL); if (owned_web_contents_) owned_web_contents_.reset(NULL); // Delete objects created by the platform delegate that may be referenced by // the WebContents. file_dialog_manager_.reset(NULL); javascript_dialog_manager_.reset(NULL); menu_manager_.reset(NULL); // Delete the platform delegate. platform_delegate_.reset(NULL); DetachAllFrames(); CefBrowserInfoManager::GetInstance()->RemoveBrowserInfo(browser_info_); browser_info_->set_browser(NULL); } #if defined(USE_AURA) views::Widget* CefBrowserHostImpl::GetWindowWidget() const { CEF_REQUIRE_UIT(); if (!platform_delegate_) return nullptr; return platform_delegate_->GetWindowWidget(); } CefRefPtr CefBrowserHostImpl::GetBrowserView() const { CEF_REQUIRE_UIT(); if (IsViewsHosted() && platform_delegate_) return platform_delegate_->GetBrowserView(); return nullptr; } #endif // defined(USE_AURA) void CefBrowserHostImpl::CancelContextMenu() { CEF_REQUIRE_UIT(); if (menu_manager_) menu_manager_->CancelContextMenu(); } CefRefPtr CefBrowserHostImpl::GetFrameForRequest( const net::URLRequest* request) { CEF_REQUIRE_IOT(); const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); if (!info) return nullptr; // The value of |IsMainFrame| is unreliable in these cases. const bool is_main_frame_state_flaky = info->IsDownload() || info->GetResourceType() == content::RESOURCE_TYPE_XHR; return GetOrCreateFrame(info->GetRenderFrameID(), info->GetFrameTreeNodeId(), CefFrameHostImpl::kUnspecifiedFrameId, info->IsMainFrame(), is_main_frame_state_flaky, base::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()) { LOG(ERROR) << "Invalid URL passed to CefBrowserHostImpl::Navigate: " << params.url; return; } request.method = params.method; request.referrer = params.referrer.url; request.referrer_policy = CefRequestImpl::BlinkReferrerPolicyToNetReferrerPolicy( params.referrer.policy); request.frame_id = params.frame_id; request.site_for_cookies = params.site_for_cookies; request.headers = params.headers; request.load_flags = params.load_flags; request.upload_data = params.upload_data; Send(new CefMsg_LoadRequest(MSG_ROUTING_NONE, request)); OnSetFocus(FOCUS_SOURCE_NAVIGATION); } void CefBrowserHostImpl::LoadRequest(int64 frame_id, CefRefPtr request) { CefNavigateParams params(GURL(), ui::PAGE_TRANSITION_TYPED); params.frame_id = frame_id; static_cast(request.get())->Get(params); Navigate(params); } void CefBrowserHostImpl::LoadURL(int64 frame_id, const std::string& url, const content::Referrer& referrer, ui::PageTransition transition, const std::string& extra_headers) { if (frame_id == CefFrameHostImpl::kMainFrameId) { // Go through the navigation controller. if (CEF_CURRENTLY_ON_UIT()) { if (navigation_locked()) { // Try again after the lock has been released. set_pending_navigation_action( base::BindOnce(&CefBrowserHostImpl::LoadURL, this, frame_id, url, referrer, transition, extra_headers)); return; } if (web_contents()) { 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()) { LOG(ERROR) << "Invalid URL passed to CefBrowserHostImpl::LoadURL: " << url; return; } web_contents()->GetController().LoadURL(gurl, referrer, transition, extra_headers); OnSetFocus(FOCUS_SOURCE_NAVIGATION); } } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::LoadURL, this, frame_id, url, referrer, transition, extra_headers)); } } else { CefNavigateParams params(GURL(url), transition); params.frame_id = frame_id; params.referrer = referrer; params.headers = extra_headers; Navigate(params); } } void CefBrowserHostImpl::LoadString(int64 frame_id, const std::string& string, const std::string& 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.AppendString(string); params.arguments.AppendString(url); Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } void CefBrowserHostImpl::SendCommand( int64 frame_id, const std::string& 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()) { TRACE_EVENT2("libcef", "CefBrowserHostImpl::SendCommand", "frame_id", frame_id, "needsResponse", responseHandler.get() ? 1 : 0); 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.AppendString(command); Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendCommand, this, frame_id, command, responseHandler)); } } void CefBrowserHostImpl::SendCode( int64 frame_id, bool is_javascript, const std::string& code, const std::string& 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()) { TRACE_EVENT2("libcef", "CefBrowserHostImpl::SendCommand", "frame_id", frame_id, "needsResponse", responseHandler.get() ? 1 : 0); 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.AppendBoolean(is_javascript); params.arguments.AppendString(code); params.arguments.AppendString(script_url); params.arguments.AppendInteger(script_start_line); Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } else { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::SendCode, this, frame_id, is_javascript, code, script_url, script_start_line, responseHandler)); } } void CefBrowserHostImpl::ExecuteJavaScriptWithUserGestureForTests( int64 frame_id, const CefString& javascript) { DCHECK(frame_id >= CefFrameHostImpl::kMainFrameId); if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce( &CefBrowserHostImpl::ExecuteJavaScriptWithUserGestureForTests, this, frame_id, javascript)); return; } if (!web_contents()) return; content::RenderFrameHost* rfh; if (frame_id == CefFrameHostImpl::kMainFrameId) { rfh = web_contents()->GetMainFrame(); } else { rfh = content::RenderFrameHost::FromID( web_contents()->GetRenderViewHost()->GetProcess()->GetID(), frame_id); } if (rfh) rfh->ExecuteJavaScriptWithUserGestureForTests(javascript); } void CefBrowserHostImpl::ViewText(const std::string& text) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ViewText, this, text)); return; } if (platform_delegate_) platform_delegate_->ViewText(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 && platform_delegate_) platform_delegate_->HandleExternalProtocol(url); } else { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::HandleExternalProtocol, this, url)); } } SkColor CefBrowserHostImpl::GetBackgroundColor() const { // Don't use |platform_delegate_| because it's not thread-safe. return CefContext::Get()->GetBackgroundColor( &settings_, is_windowless_ ? STATE_ENABLED : STATE_DISABLED); } int CefBrowserHostImpl::browser_id() const { return browser_info_->browser_id(); } content::BrowserContext* CefBrowserHostImpl::GetBrowserContext() { CEF_REQUIRE_UIT(); if (web_contents()) return web_contents()->GetBrowserContext(); return nullptr; } 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 (platform_delegate_) platform_delegate_->SendFocusEvent(true); } else { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::OnSetFocus, this, source)); } } void CefBrowserHostImpl::RunFileChooser( const CefFileDialogRunner::FileChooserParams& params, const CefFileDialogRunner::RunFileChooserCallback& callback) { EnsureFileDialogManager(); file_dialog_manager_->RunFileChooser(params, callback); } bool CefBrowserHostImpl::EmbedsFullscreenWidget() const { // When using windowless rendering do not allow Flash to create its own full- // screen widget. return IsWindowless(); } void CefBrowserHostImpl::EnterFullscreenModeForTab( content::WebContents* web_contents, const GURL& origin, const blink::WebFullscreenOptions& options) { OnFullscreenModeChange(true); } void CefBrowserHostImpl::ExitFullscreenModeForTab( content::WebContents* web_contents) { OnFullscreenModeChange(false); } bool CefBrowserHostImpl::IsFullscreenForTabOrPending( const content::WebContents* web_contents) const { return is_fullscreen_; } blink::WebDisplayMode CefBrowserHostImpl::GetDisplayMode( const content::WebContents* web_contents) const { return is_fullscreen_ ? blink::kWebDisplayModeFullscreen : blink::kWebDisplayModeBrowser; } void CefBrowserHostImpl::FindReply(content::WebContents* web_contents, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) { if (client_.get()) { CefRefPtr handler = client_->GetFindHandler(); if (handler.get()) { CefRect rect(selection_rect.x(), selection_rect.y(), selection_rect.width(), selection_rect.height()); handler->OnFindResult(this, request_id, number_of_matches, rect, active_match_ordinal, final_update); } } } void CefBrowserHostImpl::ImeSetComposition( const CefString& text, const std::vector& underlines, const CefRange& replacement_range, const CefRange& selection_range) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ImeSetComposition, this, text, underlines, replacement_range, selection_range)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->ImeSetComposition(text, underlines, replacement_range, selection_range); } void CefBrowserHostImpl::ImeCommitText(const CefString& text, const CefRange& replacement_range, int relative_cursor_pos) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ImeCommitText, this, text, replacement_range, relative_cursor_pos)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->ImeCommitText(text, replacement_range, relative_cursor_pos); } void CefBrowserHostImpl::ImeFinishComposingText(bool keep_selection) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ImeFinishComposingText, this, keep_selection)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->ImeFinishComposingText(keep_selection); } void CefBrowserHostImpl::ImeCancelComposition() { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::ImeCancelComposition, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->ImeCancelComposition(); } void CefBrowserHostImpl::DragTargetDragEnter( CefRefPtr drag_data, const CefMouseEvent& event, CefBrowserHost::DragOperationsMask allowed_ops) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragTargetDragEnter, this, drag_data, event, allowed_ops)); return; } if (!drag_data.get()) { NOTREACHED(); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragTargetDragEnter(drag_data, event, allowed_ops); } void CefBrowserHostImpl::DragTargetDragOver( const CefMouseEvent& event, CefBrowserHost::DragOperationsMask allowed_ops) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragTargetDragOver, this, event, allowed_ops)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragTargetDragOver(event, allowed_ops); } void CefBrowserHostImpl::DragTargetDragLeave() { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce( &CefBrowserHostImpl::DragTargetDragLeave, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragTargetDragLeave(); } void CefBrowserHostImpl::DragTargetDrop(const CefMouseEvent& event) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragTargetDrop, this, event)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragTargetDrop(event); } void CefBrowserHostImpl::DragSourceSystemDragEnded() { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragSourceSystemDragEnded, this)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragSourceSystemDragEnded(); } void CefBrowserHostImpl::DragSourceEndedAt( int x, int y, CefBrowserHost::DragOperationsMask op) { if (!IsWindowless()) { NOTREACHED() << "Window rendering is not disabled"; return; } if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK( CEF_UIT, base::BindOnce(&CefBrowserHostImpl::DragSourceEndedAt, this, x, y, op)); return; } if (!web_contents() || !platform_delegate_) return; platform_delegate_->DragSourceEndedAt(x, y, op); } // content::WebContentsDelegate methods. // ----------------------------------------------------------------------------- // |source| may be NULL if the navigation originates from a guest view via // CefContentBrowserClient::CanCreateWindow. content::WebContents* CefBrowserHostImpl::OpenURLFromTab( content::WebContents* source, const content::OpenURLParams& params) { bool cancel = false; if (client_.get()) { CefRefPtr handler = client_->GetRequestHandler(); if (handler.get()) { cancel = handler->OnOpenURLFromTab( this, GetFrame(params.frame_tree_node_id), params.url.spec(), static_cast(params.disposition), params.user_gesture); } } if (!cancel) { // Start a navigation in the current browser that will result in the // creation of a new render process. LoadURL(CefFrameHostImpl::kMainFrameId, params.url.spec(), params.referrer, params.transition, params.extra_headers); return source; } // We don't know where the navigation, if any, will occur. return nullptr; } bool CefBrowserHostImpl::ShouldTransferNavigation( bool is_main_frame_navigation) { if (extension_host_) { return extension_host_->ShouldTransferNavigation(is_main_frame_navigation); } return true; } void CefBrowserHostImpl::AddNewContents( content::WebContents* source, std::unique_ptr new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { CefRefPtr owner = GetBrowserForContents(new_contents.get()); if (owner) { // Taking ownership of |new_contents|. owner->set_owned_web_contents(new_contents.release()); return; } if (extension_host_) { extension_host_->AddNewContents(source, std::move(new_contents), disposition, initial_rect, user_gesture, was_blocked); } } void CefBrowserHostImpl::LoadingStateChanged(content::WebContents* source, bool to_different_document) { const int current_index = source->GetController().GetLastCommittedEntryIndex(); const int max_index = source->GetController().GetEntryCount() - 1; const bool is_loading = source->IsLoading(); const bool can_go_back = (current_index > 0); const bool can_go_forward = (current_index < max_index); { base::AutoLock lock_scope(state_lock_); // This method may be called multiple times in a row with |is_loading| true // as a result of https://crrev.com/5e750ad0. Ignore the 2nd+ times. if (is_loading_ == is_loading) return; is_loading_ = is_loading; can_go_back_ = can_go_back; can_go_forward_ = can_go_forward; } if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { handler->OnLoadingStateChange(this, is_loading, can_go_back, can_go_forward); } } } void CefBrowserHostImpl::LoadProgressChanged(content::WebContents* source, double progress) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { handler->OnLoadingProgressChange(this, progress); } } } void CefBrowserHostImpl::CloseContents(content::WebContents* source) { CEF_REQUIRE_UIT(); if (destruction_state_ == DESTRUCTION_STATE_COMPLETED) return; bool close_browser = true; // If this method is called in response to something other than // WindowDestroyed() ask the user if the browser should close. if (client_.get() && (IsWindowless() || !window_destroyed_)) { CefRefPtr handler = client_->GetLifeSpanHandler(); if (handler.get()) { close_browser = !handler->DoClose(this); } } if (close_browser) { if (destruction_state_ != DESTRUCTION_STATE_ACCEPTED) destruction_state_ = DESTRUCTION_STATE_ACCEPTED; if (!IsWindowless() && !window_destroyed_) { // A window exists so try to close it using the platform method. Will // result in a call to WindowDestroyed() if/when the window is destroyed // via the platform window destruction mechanism. platform_delegate_->CloseHostWindow(); } else { // Keep a reference to the browser while it's in the process of being // destroyed. CefRefPtr browser(this); // No window exists. Destroy the browser immediately. Don't call other // browser methods after calling DestroyBrowser(). DestroyBrowser(); } } else if (destruction_state_ != DESTRUCTION_STATE_NONE) { destruction_state_ = DESTRUCTION_STATE_NONE; } } void CefBrowserHostImpl::UpdateTargetURL(content::WebContents* source, const GURL& url) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) handler->OnStatusMessage(this, url.spec()); } } bool CefBrowserHostImpl::DidAddMessageToConsole( content::WebContents* source, int32_t level, const base::string16& message, int32_t line_no, const base::string16& source_id) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { // Use LOGSEVERITY_DEBUG for unrecognized |level| values. cef_log_severity_t log_level = LOGSEVERITY_DEBUG; switch (level) { case 0: log_level = LOGSEVERITY_INFO; break; case 1: log_level = LOGSEVERITY_WARNING; break; case 2: log_level = LOGSEVERITY_ERROR; break; } return handler->OnConsoleMessage(this, log_level, message, source_id, line_no); } } return false; } void CefBrowserHostImpl::BeforeUnloadFired(content::WebContents* source, bool proceed, bool* proceed_to_fire_unload) { if (destruction_state_ == DESTRUCTION_STATE_ACCEPTED || proceed) { *proceed_to_fire_unload = true; } else if (!proceed) { *proceed_to_fire_unload = false; destruction_state_ = DESTRUCTION_STATE_NONE; } } bool CefBrowserHostImpl::TakeFocus(content::WebContents* source, bool reverse) { if (client_.get()) { CefRefPtr handler = client_->GetFocusHandler(); if (handler.get()) handler->OnTakeFocus(this, !reverse); } return false; } bool CefBrowserHostImpl::HandleContextMenu( const content::ContextMenuParams& params) { return HandleContextMenu(web_contents(), params); } content::WebContents* CefBrowserHostImpl::GetActionableWebContents() { if (web_contents() && extensions::ExtensionsEnabled()) { content::WebContents* guest_contents = extensions::GetFullPageGuestForOwnerContents(web_contents()); if (guest_contents) return guest_contents; } return web_contents(); } KeyboardEventProcessingResult CefBrowserHostImpl::PreHandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { if (platform_delegate_ && client_.get()) { CefRefPtr handler = client_->GetKeyboardHandler(); if (handler.get()) { CefKeyEvent cef_event; if (browser_util::GetCefKeyEvent(event, cef_event)) { cef_event.focus_on_editable_field = focus_on_editable_field_; CefEventHandle event_handle = platform_delegate_->GetEventHandle(event); bool is_keyboard_shortcut = false; bool result = handler->OnPreKeyEvent(this, cef_event, event_handle, &is_keyboard_shortcut); if (result) return KeyboardEventProcessingResult::HANDLED; else if (is_keyboard_shortcut) return KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT; } } } return KeyboardEventProcessingResult::NOT_HANDLED; } 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 (!platform_delegate_) return; if (client_.get()) { CefRefPtr handler = client_->GetKeyboardHandler(); if (handler.get()) { CefKeyEvent cef_event; if (browser_util::GetCefKeyEvent(event, cef_event)) { cef_event.focus_on_editable_field = focus_on_editable_field_; CefEventHandle event_handle = platform_delegate_->GetEventHandle(event); if (handler->OnKeyEvent(this, cef_event, event_handle)) return; } } } platform_delegate_->HandleKeyboardEvent(event); } bool CefBrowserHostImpl::PreHandleGestureEvent( content::WebContents* source, const blink::WebGestureEvent& event) { if (extension_host_) return extension_host_->PreHandleGestureEvent(source, event); return false; } bool CefBrowserHostImpl::CanDragEnter(content::WebContents* source, const content::DropData& data, blink::WebDragOperationsMask mask) { CefRefPtr handler = client_->GetDragHandler(); if (handler.get()) { CefRefPtr drag_data(new CefDragDataImpl(data)); drag_data->SetReadOnly(true); if (handler->OnDragEnter( this, drag_data.get(), static_cast(mask))) { return false; } } return true; } void CefBrowserHostImpl::GetCustomWebContentsView( content::WebContents* web_contents, const GURL& target_url, int opener_render_process_id, int opener_render_frame_id, content::WebContentsView** view, content::RenderViewHostDelegateView** delegate_view) { CefBrowserInfoManager::GetInstance()->GetCustomWebContentsView( target_url, opener_render_process_id, opener_render_frame_id, view, delegate_view); } void CefBrowserHostImpl::WebContentsCreated( content::WebContents* source_contents, int opener_render_process_id, int opener_render_frame_id, const std::string& frame_name, const GURL& target_url, content::WebContents* new_contents) { CefBrowserSettings settings; CefRefPtr client; std::unique_ptr platform_delegate; CefBrowserInfoManager::GetInstance()->WebContentsCreated( target_url, opener_render_process_id, opener_render_frame_id, settings, client, platform_delegate); scoped_refptr info = CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo( new_contents, platform_delegate->IsWindowless()); DCHECK(info.get()); DCHECK(info->is_popup()); CefRefPtr opener = GetBrowserForContents(source_contents); if (!opener.get()) return; // Popups must share the same BrowserContext as the parent. CefBrowserContext* browser_context = static_cast(new_contents->GetBrowserContext()); DCHECK(browser_context); // We don't officially own |new_contents| until AddNewContents() is called. // However, we need to install observers/delegates here. CefRefPtr browser = CreateInternal(settings, client, new_contents, false, info, opener, false, browser_context->GetCefRequestContext(), std::move(platform_delegate), nullptr); } void CefBrowserHostImpl::DidNavigateMainFramePostCommit( content::WebContents* web_contents) { base::AutoLock lock_scope(state_lock_); has_document_ = false; } content::JavaScriptDialogManager* CefBrowserHostImpl::GetJavaScriptDialogManager(content::WebContents* source) { if (!javascript_dialog_manager_.get() && platform_delegate_) { javascript_dialog_manager_.reset(new CefJavaScriptDialogManager( this, platform_delegate_->CreateJavaScriptDialogRunner())); } return javascript_dialog_manager_.get(); } void CefBrowserHostImpl::RunFileChooser( content::RenderFrameHost* render_frame_host, const content::FileChooserParams& params) { EnsureFileDialogManager(); file_dialog_manager_->RunFileChooser(render_frame_host, params); } bool CefBrowserHostImpl::HandleContextMenu( content::WebContents* web_contents, const content::ContextMenuParams& params) { CEF_REQUIRE_UIT(); if (!menu_manager_.get() && platform_delegate_) { menu_manager_.reset( new CefMenuManager(this, platform_delegate_->CreateMenuRunner())); } return menu_manager_->CreateContextMenu(params); } void CefBrowserHostImpl::UpdatePreferredSize(content::WebContents* source, const gfx::Size& pref_size) { #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX)) CEF_REQUIRE_UIT(); if (platform_delegate_) platform_delegate_->SizeTo(pref_size.width(), pref_size.height()); #endif } void CefBrowserHostImpl::ResizeDueToAutoResize(content::WebContents* source, const gfx::Size& new_size) { CEF_REQUIRE_UIT(); if (client_) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler && handler->OnAutoResize( this, CefSize(new_size.width(), new_size.height()))) { return; } } UpdatePreferredSize(source, new_size); } void CefBrowserHostImpl::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) { CEF_REQUIRE_UIT(); content::MediaStreamDevices devices; const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (!command_line->HasSwitch(switches::kEnableMediaStream)) { // Cancel the request. std::move(callback).Run(devices, content::MEDIA_DEVICE_PERMISSION_DENIED, std::unique_ptr()); return; } // Based on chrome/browser/media/media_stream_devices_controller.cc bool microphone_requested = (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE); bool webcam_requested = (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE); bool screen_requested = (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE); if (microphone_requested || webcam_requested || screen_requested) { // Pick the desired device or fall back to the first available of the // given type. if (microphone_requested) { CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( request.requested_audio_device_id, true, false, &devices); } if (webcam_requested) { CefMediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( request.requested_video_device_id, false, true, &devices); } if (screen_requested) { content::DesktopMediaID media_id; if (request.requested_video_device_id.empty()) { media_id = content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, -1 /* webrtc::kFullDesktopScreenId */); } else { media_id = content::DesktopMediaID::Parse(request.requested_video_device_id); } devices.push_back(content::MediaStreamDevice( content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); } } std::move(callback).Run(devices, content::MEDIA_DEVICE_OK, std::unique_ptr()); } bool CefBrowserHostImpl::CheckMediaAccessPermission( content::RenderFrameHost* render_frame_host, const GURL& security_origin, content::MediaStreamType type) { // Check media access permission without prompting the user. This is called // when loading the Pepper Flash plugin. const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); return command_line->HasSwitch(switches::kEnableMediaStream); } bool CefBrowserHostImpl::IsNeverVisible(content::WebContents* web_contents) { if (extension_host_) return extension_host_->IsNeverVisible(web_contents); return false; } // content::WebContentsObserver methods. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::RenderFrameCreated( content::RenderFrameHost* render_frame_host) { const int render_process_id = render_frame_host->GetProcess()->GetID(); const int render_routing_id = render_frame_host->GetRoutingID(); if (!browser_info_->render_id_manager()->is_render_frame_id_match( render_process_id, render_routing_id)) { browser_info_->render_id_manager()->add_render_frame_id(render_process_id, render_routing_id); } const int frame_tree_node_id = render_frame_host->GetFrameTreeNodeId(); if (!browser_info_->frame_tree_node_id_manager()->is_frame_tree_node_id_match( frame_tree_node_id)) { browser_info_->frame_tree_node_id_manager()->add_frame_tree_node_id( frame_tree_node_id); } } void CefBrowserHostImpl::RenderFrameHostChanged( content::RenderFrameHost* old_host, content::RenderFrameHost* new_host) { // Just in case RenderFrameCreated wasn't called for some reason. RenderFrameCreated(new_host); } void CefBrowserHostImpl::FrameDeleted( content::RenderFrameHost* render_frame_host) { // The ID entries should currently exist. const int render_process_id = render_frame_host->GetProcess()->GetID(); const int render_routing_id = render_frame_host->GetRoutingID(); browser_info_->render_id_manager()->remove_render_frame_id(render_process_id, render_routing_id); const int frame_tree_node_id = render_frame_host->GetFrameTreeNodeId(); browser_info_->frame_tree_node_id_manager()->remove_frame_tree_node_id( frame_tree_node_id); if (web_contents()) { const bool is_main_frame = (render_frame_host->GetParent() == nullptr); CefBrowserContext* context = static_cast(web_contents()->GetBrowserContext()); if (context) { context->OnRenderFrameDeleted(render_process_id, render_routing_id, is_main_frame, false); } } base::AutoLock lock_scope(state_lock_); if (render_routing_id >= 0) { FrameIdMap::iterator it = frames_.find(render_routing_id); if (it != frames_.end()) { it->second->Detach(); frames_.erase(it); } if (main_frame_id_ == render_routing_id) main_frame_id_ = CefFrameHostImpl::kInvalidFrameId; if (focused_frame_id_ == render_routing_id) focused_frame_id_ = CefFrameHostImpl::kInvalidFrameId; } if (frame_tree_node_id >= 0) { FrameTreeNodeIdMap::iterator it = pending_frames_.find(frame_tree_node_id); if (it != pending_frames_.end()) { it->second->Detach(); pending_frames_.erase(it); } } } void CefBrowserHostImpl::RenderViewCreated( content::RenderViewHost* render_view_host) { // May be already registered if the renderer crashed previously. if (!registrar_->IsRegistered( this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(render_view_host))) { registrar_->Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(render_view_host)); } // RenderFrameCreated is otherwise not called for new popup browsers. RenderFrameCreated(render_view_host->GetMainFrame()); platform_delegate_->RenderViewCreated(render_view_host); } void CefBrowserHostImpl::RenderViewDeleted( content::RenderViewHost* render_view_host) { if (registrar_->IsRegistered( this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(render_view_host))) { registrar_->Remove( this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, content::Source(render_view_host)); } } void CefBrowserHostImpl::RenderViewReady() { ConfigureAutoResize(); // Send the queued messages. queue_messages_ = false; while (!queued_messages_.empty()) { Send(queued_messages_.front()); queued_messages_.pop(); } if (client_.get()) { CefRefPtr handler = client_->GetRequestHandler(); if (handler.get()) handler->OnRenderViewReady(this); } } void CefBrowserHostImpl::RenderProcessGone(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_->GetRequestHandler(); if (handler.get()) { std::unique_ptr navigation_lock = CreateNavigationLock(); handler->OnRenderProcessTerminated(this, ts); } } } void CefBrowserHostImpl::DidFinishNavigation( content::NavigationHandle* navigation_handle) { const net::Error error_code = navigation_handle->GetNetErrorCode(); // Skip calls where the navigation has not yet committed and there is no // error code. For example, when creating a browser without loading a URL. if (!navigation_handle->HasCommitted() && error_code == net::OK) return; const int64 frame_id = navigation_handle->GetRenderFrameHost() ? navigation_handle->GetRenderFrameHost()->GetRoutingID() : CefFrameHostImpl::kUnspecifiedFrameId; const bool is_main_frame = navigation_handle->IsInMainFrame(); const GURL& url = (error_code == net::OK ? navigation_handle->GetURL() : GURL()); CefRefPtr frame = GetOrCreateFrame(frame_id, navigation_handle->GetFrameTreeNodeId(), CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, false, base::string16(), url); if (error_code == net::OK) { // The navigation has been committed and there is no error. DCHECK(navigation_handle->HasCommitted()); // Don't call OnLoadStart for same page navigations (fragments, // history state). if (!navigation_handle->IsSameDocument()) OnLoadStart(frame, navigation_handle->GetPageTransition()); if (is_main_frame) OnAddressChange(frame, url); } else { // The navigation failed with an error. This may happen before commit // (e.g. network error) or after commit (e.g. response filter error). // If the error happened before commit then this call will originate from // RenderFrameHostImpl::OnDidFailProvisionalLoadWithError. // OnLoadStart/OnLoadEnd will not be called. OnLoadError(frame, navigation_handle->GetURL(), error_code); } if (web_contents()) { CefBrowserContext* context = static_cast(web_contents()->GetBrowserContext()); if (context) { context->AddVisitedURLs(navigation_handle->GetRedirectChain()); } } } void CefBrowserHostImpl::DocumentAvailableInMainFrame() { base::AutoLock lock_scope(state_lock_); has_document_ = true; } void CefBrowserHostImpl::DidFailLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) { // The navigation failed after commit. OnLoadStart was called so we also call // OnLoadEnd. const bool is_main_frame = !render_frame_host->GetParent(); CefRefPtr frame = GetOrCreateFrame(render_frame_host->GetRoutingID(), render_frame_host->GetFrameTreeNodeId(), CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, false, base::string16(), validated_url); OnLoadError(frame, validated_url, error_code); OnLoadEnd(frame, validated_url, error_code); } void CefBrowserHostImpl::TitleWasSet(content::NavigationEntry* entry) { // |entry| may be NULL if a popup is created via window.open and never // navigated. if (entry) OnTitleChange(entry->GetTitle()); else if (web_contents()) OnTitleChange(web_contents()->GetTitle()); } void CefBrowserHostImpl::PluginCrashed(const base::FilePath& plugin_path, base::ProcessId plugin_pid) { if (client_.get()) { CefRefPtr handler = client_->GetRequestHandler(); if (handler.get()) handler->OnPluginCrashed(this, plugin_path.value()); } } void CefBrowserHostImpl::DidUpdateFaviconURL( const std::vector& candidates) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) { std::vector icon_urls; std::vector::const_iterator it = candidates.begin(); for (; it != candidates.end(); ++it) { if (it->icon_type == content::FaviconURL::IconType::kFavicon) icon_urls.push_back(it->icon_url.spec()); } if (!icon_urls.empty()) handler->OnFaviconURLChange(this, icon_urls); } } } bool CefBrowserHostImpl::OnMessageReceived(const IPC::Message& message) { // Handle the cursor message here if mouse cursor change is disabled instead // of propegating the message to the normal handler. if (message.type() == ViewHostMsg_SetCursor::ID) return IsMouseCursorChangeDisabled(); bool handled = true; IPC_BEGIN_MESSAGE_MAP(CefBrowserHostImpl, message) IPC_MESSAGE_HANDLER(CefHostMsg_FrameIdentified, OnFrameIdentified) IPC_MESSAGE_HANDLER(CefHostMsg_DidFinishLoad, OnDidFinishLoad) IPC_MESSAGE_HANDLER(CefHostMsg_UpdateDraggableRegions, OnUpdateDraggableRegions) 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::OnMessageReceived( const IPC::Message& message, content::RenderFrameHost* render_frame_host) { bool handled = true; IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(CefBrowserHostImpl, message, render_frame_host) IPC_MESSAGE_HANDLER(CefHostMsg_FrameFocused, OnFrameFocused) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void CefBrowserHostImpl::AccessibilityEventReceived( const content::AXEventNotificationDetails& content_event_bundle) { // Only needed in windowless mode. if (IsWindowless()) { if (!web_contents() || !platform_delegate_) return; platform_delegate_->AccessibilityEventReceived(content_event_bundle); } } void CefBrowserHostImpl::AccessibilityLocationChangesReceived( const std::vector& locData) { // Only needed in windowless mode. if (IsWindowless()) { if (!web_contents() || !platform_delegate_) return; platform_delegate_->AccessibilityLocationChangesReceived(locData); } } void CefBrowserHostImpl::OnWebContentsFocused( content::RenderWidgetHost* render_widget_host) { if (client_.get()) { CefRefPtr handler = client_->GetFocusHandler(); if (handler.get()) handler->OnGotFocus(this); } } void CefBrowserHostImpl::AddObserver(Observer* observer) { CEF_REQUIRE_UIT(); observers_.AddObserver(observer); } void CefBrowserHostImpl::RemoveObserver(Observer* observer) { CEF_REQUIRE_UIT(); observers_.RemoveObserver(observer); } bool CefBrowserHostImpl::HasObserver(Observer* observer) const { CEF_REQUIRE_UIT(); return observers_.HasObserver(observer); } CefBrowserHostImpl::NavigationLock::NavigationLock( CefRefPtr browser) : browser_(browser) { CEF_REQUIRE_UIT(); browser_->navigation_lock_count_++; } CefBrowserHostImpl::NavigationLock::~NavigationLock() { CEF_REQUIRE_UIT(); if (--browser_->navigation_lock_count_ == 0) { if (!browser_->pending_navigation_action_.is_null()) { CEF_POST_TASK(CEF_UIT, std::move(browser_->pending_navigation_action_)); } } } std::unique_ptr CefBrowserHostImpl::CreateNavigationLock() { return base::WrapUnique(new NavigationLock(this)); } bool CefBrowserHostImpl::navigation_locked() const { CEF_REQUIRE_UIT(); return navigation_lock_count_ > 0; } void CefBrowserHostImpl::set_pending_navigation_action( base::OnceClosure action) { CEF_REQUIRE_UIT(); pending_navigation_action_ = std::move(action); } // content::WebContentsObserver::OnMessageReceived() message handlers. // ----------------------------------------------------------------------------- void CefBrowserHostImpl::OnFrameIdentified(int64 frame_id, int64 parent_frame_id, base::string16 name) { bool is_main_frame = (parent_frame_id == CefFrameHostImpl::kMainFrameId); GetOrCreateFrame(frame_id, kUnspecifiedFrameTreeNodeId, parent_frame_id, is_main_frame, false, name, GURL()); } void CefBrowserHostImpl::OnFrameFocused( content::RenderFrameHost* render_frame_host) { const int64 frame_id = render_frame_host->GetRoutingID(); CefRefPtr unfocused_frame; CefRefPtr focused_frame; { base::AutoLock lock_scope(state_lock_); if (focused_frame_id_ != CefFrameHostImpl::kInvalidFrameId) { // Unfocus the previously focused frame. FrameIdMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) unfocused_frame = it->second; } // Focus the newly focused frame. FrameIdMap::iterator it = frames_.find(frame_id); if (it != frames_.end()) focused_frame = it->second; focused_frame_id_ = focused_frame.get() ? frame_id : CefFrameHostImpl::kInvalidFrameId; } if (unfocused_frame.get()) unfocused_frame->SetFocused(false); if (focused_frame.get()) focused_frame->SetFocused(true); } void CefBrowserHostImpl::OnDidFinishLoad(int64 frame_id, const GURL& validated_url, bool is_main_frame, int http_status_code) { CefRefPtr frame = GetOrCreateFrame(frame_id, kUnspecifiedFrameTreeNodeId, CefFrameHostImpl::kUnspecifiedFrameId, is_main_frame, false, base::string16(), validated_url); // Give internal scheme handlers an opportunity to update content. scheme::DidFinishLoad(frame, validated_url); OnLoadEnd(frame, validated_url, http_status_code); } void CefBrowserHostImpl::OnUpdateDraggableRegions( const std::vector& regions) { std::vector draggable_regions; draggable_regions.reserve(regions.size()); std::vector::const_iterator it = regions.begin(); for (; it != regions.end(); ++it) { const gfx::Rect& rect(it->bounds); const CefRect bounds(rect.x(), rect.y(), rect.width(), rect.height()); draggable_regions.push_back(CefDraggableRegion(bounds, it->draggable)); } if (client_.get()) { CefRefPtr handler = client_->GetDragHandler(); if (handler.get()) { handler->OnDraggableRegionsChanged(this, draggable_regions); } } } void CefBrowserHostImpl::OnRequest(const Cef_Request_Params& params) { CEF_REQUIRE_UIT(); 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(MSG_ROUTING_NONE, response_params)); } } void CefBrowserHostImpl::OnResponse(const Cef_Response_Params& params) { CEF_REQUIRE_UIT(); response_manager_->RunHandler(params); if (params.expect_response_ack) Send(new CefMsg_ResponseAck(MSG_ROUTING_NONE, 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_LOAD_STOP || type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE); if (type == content::NOTIFICATION_LOAD_STOP) { content::NavigationController* controller = content::Source(source).ptr(); OnTitleChange(controller->GetWebContents()->GetTitle()); } else if (type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE) { focus_on_editable_field_ = *content::Details(details).ptr(); } } // CefBrowserHostImpl private methods. // ----------------------------------------------------------------------------- CefBrowserHostImpl::CefBrowserHostImpl( const CefBrowserSettings& settings, CefRefPtr client, content::WebContents* web_contents, scoped_refptr browser_info, CefRefPtr opener, CefRefPtr request_context, std::unique_ptr platform_delegate, CefRefPtr extension) : content::WebContentsObserver(web_contents), settings_(settings), client_(client), browser_info_(browser_info), opener_(kNullWindowHandle), request_context_(request_context), platform_delegate_(std::move(platform_delegate)), is_windowless_(platform_delegate_->IsWindowless()), is_views_hosted_(platform_delegate_->IsViewsHosted()), host_window_handle_(kNullWindowHandle), is_loading_(false), can_go_back_(false), can_go_forward_(false), has_document_(false), is_fullscreen_(false), queue_messages_(true), main_frame_id_(CefFrameHostImpl::kInvalidFrameId), focused_frame_id_(CefFrameHostImpl::kInvalidFrameId), destruction_state_(DESTRUCTION_STATE_NONE), window_destroyed_(false), is_in_onsetfocus_(false), focus_on_editable_field_(false), mouse_cursor_change_disabled_(false), devtools_frontend_(NULL), extension_(extension) { if (opener.get() && !platform_delegate_->IsViewsHosted()) { // GetOpenerWindowHandle() only returns a value for non-views-hosted // popup browsers. opener_ = opener->GetWindowHandle(); } DCHECK(!browser_info_->browser().get()); browser_info_->set_browser(this); web_contents->SetDelegate(this); // Associate the WebContents with this browser object. WebContentsUserDataAdapter::Register(this); registrar_.reset(new content::NotificationRegistrar); // When navigating through the history, the restored NavigationEntry's title // will be used. If the entry ends up having the same title after we return // to it, as will usually be the case, the // NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED will then be suppressed, since the // NavigationEntry's title hasn't changed. registrar_->Add(this, content::NOTIFICATION_LOAD_STOP, content::Source( &web_contents->GetController())); response_manager_.reset(new CefResponseManager); PrefsTabHelper::CreateForWebContents(web_contents); printing::CefPrintViewManager::CreateForWebContents(web_contents); if (extensions::ExtensionsEnabled()) { extensions::CefExtensionWebContentsObserver::CreateForWebContents( web_contents); // Used by the tabs extension API. zoom::ZoomController::CreateForWebContents(web_contents); } // Make sure RenderViewCreated is called at least one time. RenderViewCreated(web_contents->GetRenderViewHost()); // Associate the platform delegate with this browser. platform_delegate_->BrowserCreated(this); } void CefBrowserHostImpl::set_owned_web_contents( content::WebContents* owned_contents) { // Should not currently own a WebContents. CHECK(!owned_web_contents_); // Should already be associated with |owned_contents|. CHECK(web_contents() == owned_contents); owned_web_contents_.reset(owned_contents); } bool CefBrowserHostImpl::CreateHostWindow() { // |host_window_handle_| will not change after initial host creation for // non-views-hosted browsers. bool success = true; if (!IsWindowless()) success = platform_delegate_->CreateHostWindow(); if (success && !IsViewsHosted()) host_window_handle_ = platform_delegate_->GetHostWindowHandle(); return success; } void CefBrowserHostImpl::CreateExtensionHost( const extensions::Extension* extension, content::BrowserContext* browser_context, content::WebContents* host_contents, const GURL& url, extensions::ViewType host_type) { DCHECK(!extension_host_); // Use the *Impl context because ProcessManager expects it for notification // registration. CefBrowserContextImpl* impl_context = CefBrowserContextImpl::GetForContext(browser_context); if (host_type == extensions::VIEW_TYPE_EXTENSION_DIALOG || host_type == extensions::VIEW_TYPE_EXTENSION_POPUP) { // Create an extension host that we own. extension_host_ = new extensions::CefExtensionViewHost( this, extension, impl_context, host_contents, url, host_type); // Trigger load of the extension URL. extension_host_->CreateRenderViewSoon(); } else if (host_type == extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { is_background_host_ = true; // Create an extension host that will be owned by ProcessManager. extension_host_ = new extensions::CefExtensionBackgroundHost( this, base::BindOnce(&CefBrowserHostImpl::OnExtensionHostDeleted, this), extension, impl_context, host_contents, url, host_type); // Load will be triggered by ProcessManager::CreateBackgroundHost. } else { NOTREACHED() << " Unsupported extension host type: " << host_type; } } void CefBrowserHostImpl::DestroyExtensionHost() { if (!extension_host_) return; if (extension_host_->extension_host_type() == extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { DCHECK(is_background_host_); // Close notification for background pages arrives via CloseContents. // The extension host will be deleted by // ProcessManager::CloseBackgroundHost and OnExtensionHostDeleted will be // called to notify us. extension_host_->Close(); } else { DCHECK(!is_background_host_); // We own the extension host and must delete it. delete extension_host_; extension_host_ = nullptr; } } void CefBrowserHostImpl::OnExtensionHostDeleted() { DCHECK(is_background_host_); DCHECK(extension_host_); extension_host_ = nullptr; } CefRefPtr CefBrowserHostImpl::GetOrCreateFrame( int64 frame_id, int frame_tree_node_id, int64 parent_frame_id, bool is_main_frame, bool is_main_frame_state_flaky, base::string16 frame_name, const GURL& frame_url) { // We need either a valid |frame_id| or a valid |frame_tree_node_id|. DCHECK(frame_id >= 0 || frame_tree_node_id >= kUnusedFrameTreeNodeId); CefString url; if (frame_url.is_valid()) url = frame_url.spec(); CefString name; if (!frame_name.empty()) name = frame_name; CefRefPtr frame; bool frame_created = false; base::AutoLock lock_scope(state_lock_); if (frame_id < 0) { // With PlzNavigate the renderer process representation might not exist yet. if ((is_main_frame || is_main_frame_state_flaky) && main_frame_id_ != CefFrameHostImpl::kInvalidFrameId) { // Operating in the main frame. Continue using the existing main frame // object until the new renderer process representation is created. frame_id = main_frame_id_; } else { if (is_main_frame || is_main_frame_state_flaky) { // Always use the same pending object for the main frame. frame_tree_node_id = kMainFrameTreeNodeId; } // Operating in a sub-frame, or the main frame hasn't yet navigated for // the first time. Use a pending object keyed on |frame_tree_node_id|. frame = GetOrCreatePendingFrame(frame_tree_node_id, parent_frame_id, &frame_created); } } if (!frame) { // Delete the pending object, if any. { FrameTreeNodeIdMap::iterator it = pending_frames_.find(frame_tree_node_id); if (it != pending_frames_.end()) { DCHECK_EQ(is_main_frame, it->second->IsMain()); // Persist URL and name to the new frame. if (url.empty()) url = it->second->GetURL(); if (name.empty()) name = it->second->GetName(); pending_frames_.erase(it); } } // Update the main frame object if the ID has changed. if (is_main_frame && main_frame_id_ != frame_id) { if (main_frame_id_ != CefFrameHostImpl::kInvalidFrameId) { // Remove the old main frame object before adding the new one. FrameIdMap::iterator it = frames_.find(main_frame_id_); if (it != frames_.end()) { // Persist URL and name to the new main frame. if (url.empty()) url = it->second->GetURL(); if (name.empty()) name = it->second->GetName(); it->second->Detach(); frames_.erase(it); } if (focused_frame_id_ == main_frame_id_) focused_frame_id_ = frame_id; } main_frame_id_ = frame_id; } // Check if a frame object already exists for the ID. If so, re-use it. { FrameIdMap::const_iterator it = frames_.find(frame_id); if (it != frames_.end()) frame = it->second; } if (!frame) { frame = new CefFrameHostImpl(this, frame_id, is_main_frame, url, name, parent_frame_id); frame_created = true; frames_.insert(std::make_pair(frame_id, frame)); } } if (!frame_created && !is_main_frame_state_flaky) frame->SetAttributes(is_main_frame, url, name, parent_frame_id); #if DCHECK_IS_ON() // The main frame should always be correctly attributed. DCHECK(main_frame_id_ == CefFrameHostImpl::kInvalidFrameId || main_frame_id_ >= 0) << main_frame_id_; if (main_frame_id_ == CefFrameHostImpl::kInvalidFrameId) { // With PlzNavigate the renderer process representation might not exist yet. DCHECK(frame_id == CefFrameHostImpl::kMainFrameId || frame_id == CefFrameHostImpl::kUnspecifiedFrameId) << frame_id; DCHECK(frame->IsMain()); } else if (main_frame_id_ == frame_id) { DCHECK(frame->IsMain()); } else { DCHECK(!frame->IsMain()); } #endif return frame.get(); } CefRefPtr CefBrowserHostImpl::GetOrCreatePendingFrame( int frame_tree_node_id, int64 parent_frame_id, bool* created) { const bool is_main_frame = (frame_tree_node_id == kMainFrameTreeNodeId); DCHECK(is_main_frame || frame_tree_node_id >= 0); state_lock_.AssertAcquired(); FrameTreeNodeIdMap::const_iterator it = pending_frames_.find(frame_tree_node_id); if (it != pending_frames_.end()) { DCHECK_EQ(is_main_frame, it->second->IsMain()); return it->second; } CefRefPtr frame = new CefFrameHostImpl( this, CefFrameHostImpl::kInvalidFrameId, is_main_frame, CefString(), CefString(), parent_frame_id); pending_frames_.insert(std::make_pair(frame_tree_node_id, frame)); if (created) *created = true; return frame; } void CefBrowserHostImpl::DetachAllFrames() { FrameIdMap frames; FrameTreeNodeIdMap pending_frames; { base::AutoLock lock_scope(state_lock_); frames = frames_; frames_.clear(); pending_frames = pending_frames_; pending_frames_.clear(); if (main_frame_id_ != CefFrameHostImpl::kInvalidFrameId) main_frame_id_ = CefFrameHostImpl::kInvalidFrameId; if (focused_frame_id_ != CefFrameHostImpl::kInvalidFrameId) focused_frame_id_ = CefFrameHostImpl::kInvalidFrameId; } { FrameIdMap::const_iterator it = frames.begin(); for (; it != frames.end(); ++it) it->second->Detach(); } { FrameTreeNodeIdMap::const_iterator it = pending_frames.begin(); for (; it != pending_frames.end(); ++it) it->second->Detach(); } } gfx::Point CefBrowserHostImpl::GetScreenPoint(const gfx::Point& view) const { CEF_REQUIRE_UIT(); if (platform_delegate_) return platform_delegate_->GetScreenPoint(view); return gfx::Point(); } void CefBrowserHostImpl::StartDragging( const content::DropData& drop_data, blink::WebDragOperationsMask allowed_ops, const gfx::ImageSkia& image, const gfx::Vector2d& image_offset, const content::DragEventSourceInfo& event_info, content::RenderWidgetHostImpl* source_rwh) { if (platform_delegate_) { platform_delegate_->StartDragging(drop_data, allowed_ops, image, image_offset, event_info, source_rwh); } } void CefBrowserHostImpl::UpdateDragCursor(blink::WebDragOperation operation) { if (platform_delegate_) platform_delegate_->UpdateDragCursor(operation); } 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, ui::PageTransition transition_type) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { // Notify the handler that loading has started. handler->OnLoadStart(this, frame, static_cast(transition_type)); } } } void CefBrowserHostImpl::OnLoadError(CefRefPtr frame, const GURL& url, int error_code) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) { std::unique_ptr navigation_lock = CreateNavigationLock(); // Notify the handler that loading has failed. handler->OnLoadError(this, frame, static_cast(error_code), net::ErrorToShortString(error_code), url.spec()); } } } void CefBrowserHostImpl::OnLoadEnd(CefRefPtr frame, const GURL& url, int http_status_code) { if (client_.get()) { CefRefPtr handler = client_->GetLoadHandler(); if (handler.get()) handler->OnLoadEnd(this, frame, http_status_code); } } void CefBrowserHostImpl::OnFullscreenModeChange(bool fullscreen) { if (is_fullscreen_ == fullscreen) return; is_fullscreen_ = fullscreen; WasResized(); if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) handler->OnFullscreenModeChange(this, fullscreen); } } void CefBrowserHostImpl::OnTitleChange(const base::string16& title) { if (client_.get()) { CefRefPtr handler = client_->GetDisplayHandler(); if (handler.get()) handler->OnTitleChange(this, title); } } void CefBrowserHostImpl::OnDevToolsWebContentsDestroyed() { devtools_observer_.reset(); devtools_frontend_ = NULL; } void CefBrowserHostImpl::EnsureFileDialogManager() { CEF_REQUIRE_UIT(); if (!file_dialog_manager_.get() && platform_delegate_) { file_dialog_manager_.reset(new CefFileDialogManager( this, platform_delegate_->CreateFileDialogRunner())); } } void CefBrowserHostImpl::ConfigureAutoResize() { CEF_REQUIRE_UIT(); if (!web_contents() || !web_contents()->GetRenderWidgetHostView()) { return; } if (auto_resize_enabled_) { web_contents()->GetRenderWidgetHostView()->EnableAutoResize( auto_resize_min_, auto_resize_max_); } else { web_contents()->GetRenderWidgetHostView()->DisableAutoResize(gfx::Size()); } } bool CefBrowserHostImpl::Send(IPC::Message* message) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(base::IgnoreResult(&CefBrowserHostImpl::Send), this, message)); return true; } if (queue_messages_) { queued_messages_.push(message); return true; } else if (web_contents()) { content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); if (rvh) { message->set_routing_id(rvh->GetRoutingID()); return rvh->Send(message); } } delete message; return false; }