// Copyright 2020 The Chromium Embedded Framework Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "libcef/browser/chrome/chrome_browser_host_impl.h" #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/chrome/browser_platform_delegate_chrome.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/views/browser_view_impl.h" #include "libcef/features/runtime_checks.h" #include "base/logging.h" #include "base/notreached.h" #include "chrome/browser/printing/print_view_manager_common.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/pref_names.h" #include "components/url_formatter/url_fixer.h" #include "url/url_constants.h" #if defined(TOOLKIT_VIEWS) #include "libcef/browser/chrome/views/chrome_browser_frame.h" #include "libcef/browser/chrome/views/chrome_browser_view.h" #endif // static CefRefPtr ChromeBrowserHostImpl::Create( const CefBrowserCreateParams& params) { auto browser = CreateBrowser(params); GURL url = params.url; if (url.is_empty()) { // Chrome will navigate to kChromeUINewTabURL by default. We want to keep // the current CEF behavior of not navigating at all. Use a special URL that // will be recognized in HandleNonNavigationAboutURL. url = GURL("chrome://ignore/"); } // Add a new tab. This will indirectly create a new tab WebContents and // call ChromeBrowserDelegate::OnWebContentsCreated to create the associated // ChromeBrowserHostImpl. chrome::AddTabAt(browser, url, /*index=*/TabStripModel::kNoTab, /*foreground=*/true); // The new tab WebContents. auto web_contents = browser->tab_strip_model()->GetActiveWebContents(); CHECK(web_contents); // The associated ChromeBrowserHostImpl. auto browser_host = ChromeBrowserHostImpl::GetBrowserForContents(web_contents); CHECK(browser_host); browser->window()->Show(); return browser_host; } // static CefRefPtr ChromeBrowserHostImpl::GetBrowserForHost( const content::RenderViewHost* host) { REQUIRE_CHROME_RUNTIME(); auto browser = CefBrowserHostBase::GetBrowserForHost(host); return static_cast(browser.get()); } // static CefRefPtr ChromeBrowserHostImpl::GetBrowserForHost( const content::RenderFrameHost* host) { REQUIRE_CHROME_RUNTIME(); auto browser = CefBrowserHostBase::GetBrowserForHost(host); return static_cast(browser.get()); } // static CefRefPtr ChromeBrowserHostImpl::GetBrowserForContents( const content::WebContents* contents) { REQUIRE_CHROME_RUNTIME(); auto browser = CefBrowserHostBase::GetBrowserForContents(contents); return static_cast(browser.get()); } // static CefRefPtr ChromeBrowserHostImpl::GetBrowserForFrameTreeNode(int frame_tree_node_id) { REQUIRE_CHROME_RUNTIME(); auto browser = CefBrowserHostBase::GetBrowserForFrameTreeNode(frame_tree_node_id); return static_cast(browser.get()); } // static CefRefPtr ChromeBrowserHostImpl::GetBrowserForFrameRoute( int render_process_id, int render_routing_id) { REQUIRE_CHROME_RUNTIME(); auto browser = CefBrowserHostBase::GetBrowserForFrameRoute(render_process_id, render_routing_id); return static_cast(browser.get()); } ChromeBrowserHostImpl::~ChromeBrowserHostImpl() = default; void ChromeBrowserHostImpl::AddNewContents( std::unique_ptr contents) { DCHECK(contents); DCHECK(!browser_); // We should already be associated with the WebContents. DCHECK_EQ(GetWebContents(), contents.get()); CefBrowserCreateParams params; params.request_context = request_context(); #if defined(TOOLKIT_VIEWS) params.browser_view = GetBrowserView(); #endif // Create the new Browser representation. auto browser = CreateBrowser(params); // Add the WebContents to the Browser. browser->tab_strip_model()->AddWebContents( std::move(contents), /*index=*/TabStripModel::kNoTab, ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, TabStripModel::ADD_ACTIVE); SetBrowser(browser); browser->window()->Show(); } void ChromeBrowserHostImpl::OnWebContentsDestroyed( content::WebContents* web_contents) { platform_delegate_->WebContentsDestroyed(web_contents); DestroyBrowser(); } void ChromeBrowserHostImpl::OnSetFocus(cef_focus_source_t source) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::OnSetFocus, this, source)); return; } if (contents_delegate_->OnSetFocus(source)) return; if (browser_) { const int tab_index = GetCurrentTabIndex(); if (tab_index != TabStripModel::kNoTab) { chrome::SelectNumberedTab(browser_, tab_index); } } } void ChromeBrowserHostImpl::CloseBrowser(bool force_close) { // Always do this asynchronously because TabStripModel is not re-entrant. CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::DoCloseBrowser, this, force_close)); } bool ChromeBrowserHostImpl::TryCloseBrowser() { // TODO(chrome): Handle the case where the browser may not close immediately. CloseBrowser(true); return true; } void ChromeBrowserHostImpl::SetFocus(bool focus) { if (focus) { OnSetFocus(FOCUS_SOURCE_SYSTEM); } } CefWindowHandle ChromeBrowserHostImpl::GetWindowHandle() { NOTIMPLEMENTED(); return kNullWindowHandle; } CefWindowHandle ChromeBrowserHostImpl::GetOpenerWindowHandle() { NOTIMPLEMENTED(); return kNullWindowHandle; } double ChromeBrowserHostImpl::GetZoomLevel() { NOTIMPLEMENTED(); return 0.0; } void ChromeBrowserHostImpl::SetZoomLevel(double zoomLevel) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::RunFileDialog( FileDialogMode mode, const CefString& title, const CefString& default_file_path, const std::vector& accept_filters, int selected_accept_filter, CefRefPtr callback) { NOTIMPLEMENTED(); callback->OnFileDialogDismissed(0, {}); } void ChromeBrowserHostImpl::Print() { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, base::BindOnce(&ChromeBrowserHostImpl::Print, this)); return; } if (browser_) { // Like chrome::Print() but specifying the WebContents. printing::StartPrint(GetWebContents(), /*print_renderer=*/mojo::NullAssociatedRemote(), browser_->profile()->GetPrefs()->GetBoolean( prefs::kPrintPreviewDisabled), /*has_selection=*/false); } } void ChromeBrowserHostImpl::PrintToPDF( const CefString& path, const CefPdfPrintSettings& settings, CefRefPtr callback) { NOTIMPLEMENTED(); callback->OnPdfPrintFinished(CefString(), false); } void ChromeBrowserHostImpl::Find(int identifier, const CefString& searchText, bool forward, bool matchCase, bool findNext) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::StopFinding(bool clearSelection) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo, CefRefPtr client, const CefBrowserSettings& settings, const CefPoint& inspect_element_at) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::CloseDevTools() { NOTIMPLEMENTED(); } bool ChromeBrowserHostImpl::HasDevTools() { NOTIMPLEMENTED(); return false; } bool ChromeBrowserHostImpl::IsWindowRenderingDisabled() { return false; } void ChromeBrowserHostImpl::WasResized() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::WasHidden(bool hidden) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::NotifyScreenInfoChanged() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::Invalidate(PaintElementType type) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SendExternalBeginFrame() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SendTouchEvent(const CefTouchEvent& event) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SendFocusEvent(bool setFocus) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SendCaptureLostEvent() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::NotifyMoveOrResizeStarted() { NOTIMPLEMENTED(); } int ChromeBrowserHostImpl::GetWindowlessFrameRate() { return 0; } void ChromeBrowserHostImpl::SetWindowlessFrameRate(int frame_rate) {} void ChromeBrowserHostImpl::ImeSetComposition( const CefString& text, const std::vector& underlines, const CefRange& replacement_range, const CefRange& selection_range) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::ImeCommitText(const CefString& text, const CefRange& replacement_range, int relative_cursor_pos) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::ImeFinishComposingText(bool keep_selection) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::ImeCancelComposition() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragTargetDragEnter( CefRefPtr drag_data, const CefMouseEvent& event, DragOperationsMask allowed_ops) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragTargetDragOver(const CefMouseEvent& event, DragOperationsMask allowed_ops) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragTargetDragLeave() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragTargetDrop(const CefMouseEvent& event) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragSourceSystemDragEnded() { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::DragSourceEndedAt(int x, int y, DragOperationsMask op) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SetAudioMuted(bool mute) { NOTIMPLEMENTED(); } bool ChromeBrowserHostImpl::IsAudioMuted() { NOTIMPLEMENTED(); return false; } void ChromeBrowserHostImpl::SetAccessibilityState( cef_state_t accessibility_state) { NOTIMPLEMENTED(); } void ChromeBrowserHostImpl::SetAutoResizeEnabled(bool enabled, const CefSize& min_size, const CefSize& max_size) { NOTIMPLEMENTED(); } CefRefPtr ChromeBrowserHostImpl::GetExtension() { return nullptr; } bool ChromeBrowserHostImpl::IsBackgroundHost() { return false; } bool ChromeBrowserHostImpl::Navigate(const content::OpenURLParams& params) { CEF_REQUIRE_UIT(); if (GetCurrentTabIndex() == TabStripModel::kNoTab) { // We can't navigate via the Browser because we don't have a current tab. return CefBrowserHostBase::Navigate(params); } if (browser_) { // Fix common problems with user-typed text. Among other things, this: // - Converts absolute file paths to "file://" URLs. // - Normalizes "about:" and "chrome:" to "chrome://" URLs. // - Adds the "http://" scheme if none was specified. GURL gurl = url_formatter::FixupURL(params.url.possibly_invalid_spec(), std::string()); if (!gurl.is_valid()) { LOG(ERROR) << "Invalid URL: " << params.url.possibly_invalid_spec(); return false; } // This is generally equivalent to calling Browser::OpenURL, except: // 1. It doesn't trigger a call to CefRequestHandler::OnOpenURLFromTab, and // 2. It navigates in this CefBrowserHost's WebContents instead of // (a) creating a new WebContents, or (b) using the Browser's active // WebContents (which may not be the same), and // 3. There is no risk of triggering chrome's popup blocker. NavigateParams nav_params(browser_, gurl, params.transition); nav_params.FillNavigateParamsFromOpenURLParams(params); // Always navigate in the current tab. nav_params.disposition = WindowOpenDisposition::CURRENT_TAB; nav_params.source_contents = GetWebContents(); nav_params.tabstrip_add_types = TabStripModel::ADD_NONE; if (params.user_gesture) nav_params.window_action = NavigateParams::SHOW_WINDOW; ::Navigate(&nav_params); return true; } return false; } ChromeBrowserHostImpl::ChromeBrowserHostImpl( const CefBrowserSettings& settings, CefRefPtr client, std::unique_ptr platform_delegate, scoped_refptr browser_info, CefRefPtr request_context) : CefBrowserHostBase(settings, client, std::move(platform_delegate), browser_info, request_context) {} // static Browser* ChromeBrowserHostImpl::CreateBrowser( const CefBrowserCreateParams& params) { // Get or create the request context and profile. CefRefPtr request_context_impl = CefRequestContextImpl::GetOrCreateForRequestContext( params.request_context); CHECK(request_context_impl); auto cef_browser_context = request_context_impl->GetBrowserContext(); CHECK(cef_browser_context); auto profile = cef_browser_context->AsProfile(); Browser::CreateParams chrome_params = Browser::CreateParams(profile, /*user_gesture=*/false); // Pass |params| to cef::BrowserDelegate::Create from the Browser constructor. chrome_params.cef_params = base::MakeRefCounted(params); #if defined(TOOLKIT_VIEWS) // Configure Browser creation to use the existing Views-based // Widget/BrowserFrame (ChromeBrowserFrame) and BrowserView/BrowserWindow // (ChromeBrowserView). See views/chrome_browser_frame.h for related // documentation. ChromeBrowserView* chrome_browser_view = nullptr; if (params.browser_view) { // Don't show most controls. chrome_params.type = Browser::TYPE_POPUP; // Don't show title bar or address. chrome_params.trusted_source = true; auto view_impl = static_cast(params.browser_view.get()); chrome_browser_view = static_cast(view_impl->root_view()); chrome_params.window = chrome_browser_view; auto chrome_widget = static_cast(chrome_browser_view->GetWidget()); chrome_browser_view->set_frame(chrome_widget); } #endif // defined(TOOLKIT_VIEWS) // Create the Browser. This will indirectly create the ChomeBrowserDelegate. // The same params will be used to create a new Browser if the tab is dragged // out of the existing Browser. The returned Browser is owned by the // associated BrowserView. auto browser = Browser::Create(chrome_params); #if defined(TOOLKIT_VIEWS) if (chrome_browser_view) { // Initialize the BrowserFrame and BrowserView and create the controls that // require access to the Browser. chrome_browser_view->InitBrowser(base::WrapUnique(browser), params.browser_view); } #endif return browser; } void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, CefRefPtr opener) { DCHECK(web_contents); if (opener) { // 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=*/false); } platform_delegate_->WebContentsCreated(web_contents, /*own_web_contents=*/false); contents_delegate_->ObserveWebContents(web_contents); // Associate the platform delegate with this browser. platform_delegate_->BrowserCreated(this); // Associate the base class with the WebContents. InitializeBrowser(); // 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. { // The WebContents won't be added to the Browser's TabStripModel until later // in the current call stack. Block navigation until that time. auto navigation_lock = browser_info_->CreateNavigationLock(); OnAfterCreated(); } // 2. Notify the platform delegate. With Views this will result in a call to // CefBrowserViewDelegate::OnBrowserCreated(). 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( this, /*is_devtools_popup=*/false); } } void ChromeBrowserHostImpl::SetBrowser(Browser* browser) { CEF_REQUIRE_UIT(); browser_ = browser; static_cast(platform_delegate_.get()) ->set_chrome_browser(browser); } void ChromeBrowserHostImpl::WindowDestroyed() { CEF_REQUIRE_UIT(); #if defined(TOOLKIT_VIEWS) if (browser_ && is_views_hosted_) { auto chrome_browser_view = static_cast(browser_->window()); chrome_browser_view->Destroyed(); } #endif platform_delegate_->CloseHostWindow(); } void ChromeBrowserHostImpl::DestroyBrowser() { CEF_REQUIRE_UIT(); browser_ = nullptr; OnBeforeClose(); OnBrowserDestroyed(); // Disassociate the platform delegate from this browser. platform_delegate_->BrowserDestroyed(this); CefBrowserHostBase::DestroyBrowser(); } void ChromeBrowserHostImpl::DoCloseBrowser(bool force_close) { CEF_REQUIRE_UIT(); if (browser_) { // Like chrome::CloseTab() but specifying the WebContents. const int tab_index = GetCurrentTabIndex(); if (tab_index != TabStripModel::kNoTab) { // TODO(chrome): Handle the case where this method returns false, // indicating that the contents were not closed immediately. browser_->tab_strip_model()->CloseWebContentsAt( tab_index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB | TabStripModel::CLOSE_USER_GESTURE); } } } int ChromeBrowserHostImpl::GetCurrentTabIndex() const { CEF_REQUIRE_UIT(); if (browser_) { return browser_->tab_strip_model()->GetIndexOfWebContents(GetWebContents()); } return TabStripModel::kNoTab; }