| // 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/browser_host_base.h" |
| |
| #include <tuple> |
| |
| #include "libcef/browser/browser_info_manager.h" |
| #include "libcef/browser/browser_platform_delegate.h" |
| #include "libcef/browser/context.h" |
| #include "libcef/browser/image_impl.h" |
| #include "libcef/browser/navigation_entry_impl.h" |
| #include "libcef/browser/printing/print_util.h" |
| #include "libcef/browser/thread_util.h" |
| #include "libcef/common/frame_util.h" |
| #include "libcef/common/net/url_util.h" |
| |
| #include "base/logging.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/spellchecker/spellcheck_factory.h" |
| #include "chrome/browser/spellchecker/spellcheck_service.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "components/favicon/core/favicon_url.h" |
| #include "components/spellcheck/common/spellcheck_features.h" |
| #include "components/zoom/page_zoom.h" |
| #include "components/zoom/zoom_controller.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/download_request_utils.h" |
| #include "content/public/browser/file_select_listener.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "ui/base/resource/resource_scale_factor.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/shell_dialogs/select_file_policy.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "components/spellcheck/browser/spellcheck_platform.h" |
| #endif |
| |
| namespace { |
| |
| // Associates a CefBrowserHostBase 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(CefRefPtr<CefBrowserHostBase> browser) { |
| new WebContentsUserDataAdapter(browser); |
| } |
| |
| static CefRefPtr<CefBrowserHostBase> Get( |
| const content::WebContents* web_contents) { |
| WebContentsUserDataAdapter* adapter = |
| static_cast<WebContentsUserDataAdapter*>( |
| web_contents->GetUserData(UserDataKey())); |
| if (adapter) { |
| return adapter->browser_; |
| } |
| return nullptr; |
| } |
| |
| private: |
| WebContentsUserDataAdapter(CefRefPtr<CefBrowserHostBase> browser) |
| : browser_(browser) { |
| auto web_contents = browser->GetWebContents(); |
| DCHECK(web_contents); |
| 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<void*>(&data_key); |
| } |
| |
| CefRefPtr<CefBrowserHostBase> browser_; |
| }; |
| |
| } // namespace |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> CefBrowserHostBase::GetBrowserForHost( |
| const content::RenderViewHost* host) { |
| DCHECK(host); |
| CEF_REQUIRE_UIT(); |
| content::WebContents* web_contents = content::WebContents::FromRenderViewHost( |
| const_cast<content::RenderViewHost*>(host)); |
| if (web_contents) { |
| return GetBrowserForContents(web_contents); |
| } |
| return nullptr; |
| } |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> CefBrowserHostBase::GetBrowserForHost( |
| const content::RenderFrameHost* host) { |
| DCHECK(host); |
| CEF_REQUIRE_UIT(); |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderFrameHost( |
| const_cast<content::RenderFrameHost*>(host)); |
| if (web_contents) { |
| return GetBrowserForContents(web_contents); |
| } |
| return nullptr; |
| } |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> CefBrowserHostBase::GetBrowserForContents( |
| const content::WebContents* contents) { |
| DCHECK(contents); |
| CEF_REQUIRE_UIT(); |
| return WebContentsUserDataAdapter::Get(contents); |
| } |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> CefBrowserHostBase::GetBrowserForGlobalId( |
| const content::GlobalRenderFrameHostId& global_id) { |
| if (!frame_util::IsValidGlobalId(global_id)) { |
| return nullptr; |
| } |
| |
| if (CEF_CURRENTLY_ON_UIT()) { |
| // Use the non-thread-safe but potentially faster approach. |
| content::RenderFrameHost* render_frame_host = |
| content::RenderFrameHost::FromID(global_id); |
| if (!render_frame_host) { |
| return nullptr; |
| } |
| return GetBrowserForHost(render_frame_host); |
| } else { |
| // Use the thread-safe approach. |
| bool is_guest_view = false; |
| auto info = CefBrowserInfoManager::GetInstance()->GetBrowserInfo( |
| global_id, &is_guest_view); |
| if (info && !is_guest_view) { |
| auto browser = info->browser(); |
| if (!browser) { |
| LOG(WARNING) << "Found browser id " << info->browser_id() |
| << " but no browser object matching frame " |
| << frame_util::GetFrameDebugString(global_id); |
| } |
| return browser; |
| } |
| return nullptr; |
| } |
| } |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> |
| CefBrowserHostBase::GetBrowserForTopLevelNativeWindow( |
| gfx::NativeWindow owning_window) { |
| DCHECK(owning_window); |
| CEF_REQUIRE_UIT(); |
| |
| for (const auto& browser_info : |
| CefBrowserInfoManager::GetInstance()->GetBrowserInfoList()) { |
| if (auto browser = browser_info->browser()) { |
| if (browser->GetTopLevelNativeWindow() == owning_window) { |
| return browser; |
| } |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| // static |
| CefRefPtr<CefBrowserHostBase> CefBrowserHostBase::GetLikelyFocusedBrowser() { |
| CEF_REQUIRE_UIT(); |
| |
| for (const auto& browser_info : |
| CefBrowserInfoManager::GetInstance()->GetBrowserInfoList()) { |
| if (auto browser = browser_info->browser()) { |
| if (browser->IsFocused()) { |
| return browser; |
| } |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| CefBrowserHostBase::CefBrowserHostBase( |
| const CefBrowserSettings& settings, |
| CefRefPtr<CefClient> client, |
| std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate, |
| scoped_refptr<CefBrowserInfo> browser_info, |
| CefRefPtr<CefRequestContextImpl> request_context) |
| : settings_(settings), |
| client_(client), |
| platform_delegate_(std::move(platform_delegate)), |
| browser_info_(browser_info), |
| request_context_(request_context), |
| is_views_hosted_(platform_delegate_->IsViewsHosted()) { |
| CEF_REQUIRE_UIT(); |
| DCHECK(!browser_info_->browser().get()); |
| browser_info_->SetBrowser(this); |
| |
| contents_delegate_ = |
| std::make_unique<CefBrowserContentsDelegate>(browser_info_); |
| contents_delegate_->AddObserver(this); |
| } |
| |
| void CefBrowserHostBase::InitializeBrowser() { |
| CEF_REQUIRE_UIT(); |
| |
| // Associate the WebContents with this browser object. |
| DCHECK(GetWebContents()); |
| WebContentsUserDataAdapter::Register(this); |
| } |
| |
| void CefBrowserHostBase::DestroyBrowser() { |
| CEF_REQUIRE_UIT(); |
| |
| devtools_manager_.reset(); |
| media_stream_registrar_.reset(); |
| |
| platform_delegate_.reset(); |
| |
| contents_delegate_->RemoveObserver(this); |
| contents_delegate_->ObserveWebContents(nullptr); |
| |
| CefBrowserInfoManager::GetInstance()->RemoveBrowserInfo(browser_info_); |
| browser_info_->SetBrowser(nullptr); |
| } |
| |
| CefRefPtr<CefBrowser> CefBrowserHostBase::GetBrowser() { |
| return this; |
| } |
| |
| CefRefPtr<CefClient> CefBrowserHostBase::GetClient() { |
| return client_; |
| } |
| |
| CefRefPtr<CefRequestContext> CefBrowserHostBase::GetRequestContext() { |
| return request_context_; |
| } |
| |
| bool CefBrowserHostBase::CanZoom(cef_zoom_command_t command) { |
| // Verify that this method is being called on the UI thread. |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| DCHECK(false) << "called on invalid thread"; |
| return false; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| switch (command) { |
| case CEF_ZOOM_COMMAND_OUT: |
| return chrome::CanZoomOut(web_contents); |
| case CEF_ZOOM_COMMAND_RESET: |
| return chrome::CanResetZoom(web_contents); |
| case CEF_ZOOM_COMMAND_IN: |
| return chrome::CanZoomIn(web_contents); |
| } |
| } |
| |
| return false; |
| } |
| |
| void CefBrowserHostBase::Zoom(cef_zoom_command_t command) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::Zoom, this, command)); |
| return; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| const content::PageZoom page_zoom = [command]() { |
| switch (command) { |
| case CEF_ZOOM_COMMAND_OUT: |
| return content::PAGE_ZOOM_OUT; |
| case CEF_ZOOM_COMMAND_RESET: |
| return content::PAGE_ZOOM_RESET; |
| case CEF_ZOOM_COMMAND_IN: |
| return content::PAGE_ZOOM_IN; |
| } |
| }(); |
| |
| // Same implementation as chrome::Zoom(), but explicitly specifying the |
| // WebContents. |
| zoom::PageZoom::Zoom(web_contents, page_zoom); |
| } |
| } |
| |
| double CefBrowserHostBase::GetDefaultZoomLevel() { |
| // Verify that this method is being called on the UI thread. |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| DCHECK(false) << "called on invalid thread"; |
| return 0.0; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| zoom::ZoomController* zoom_controller = |
| zoom::ZoomController::FromWebContents(web_contents); |
| if (zoom_controller) { |
| return zoom_controller->GetDefaultZoomLevel(); |
| } |
| } |
| |
| return 0.0; |
| } |
| |
| double CefBrowserHostBase::GetZoomLevel() { |
| // Verify that this method is being called on the UI thread. |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| DCHECK(false) << "called on invalid thread"; |
| return 0.0; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| zoom::ZoomController* zoom_controller = |
| zoom::ZoomController::FromWebContents(web_contents); |
| if (zoom_controller) { |
| return zoom_controller->GetZoomLevel(); |
| } |
| } |
| |
| return 0.0; |
| } |
| |
| void CefBrowserHostBase::SetZoomLevel(double zoomLevel) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::SetZoomLevel, |
| this, zoomLevel)); |
| return; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| zoom::ZoomController* zoom_controller = |
| zoom::ZoomController::FromWebContents(web_contents); |
| if (zoom_controller) { |
| if (zoomLevel == 0.0) { |
| // Same logic as PageZoom::Zoom(PAGE_ZOOM_RESET). |
| zoomLevel = zoom_controller->GetDefaultZoomLevel(); |
| web_contents->SetPageScale(1.f); |
| } |
| zoom_controller->SetZoomLevel(zoomLevel); |
| } |
| } |
| } |
| |
| bool CefBrowserHostBase::HasView() { |
| return is_views_hosted_; |
| } |
| |
| void CefBrowserHostBase::SetFocus(bool focus) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::SetFocus, this, focus)); |
| return; |
| } |
| |
| if (focus) { |
| OnSetFocus(FOCUS_SOURCE_SYSTEM); |
| } else if (platform_delegate_) { |
| platform_delegate_->SetFocus(false); |
| } |
| } |
| |
| void CefBrowserHostBase::RunFileDialog( |
| FileDialogMode mode, |
| const CefString& title, |
| const CefString& default_file_path, |
| const std::vector<CefString>& accept_filters, |
| CefRefPtr<CefRunFileDialogCallback> callback) { |
| DCHECK(callback); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::RunFileDialog, |
| this, mode, title, default_file_path, |
| accept_filters, callback)); |
| return; |
| } |
| |
| if (!callback || !EnsureFileDialogManager()) { |
| LOG(ERROR) << "File dialog canceled due to invalid state."; |
| if (callback) { |
| callback->OnFileDialogDismissed({}); |
| } |
| return; |
| } |
| |
| file_dialog_manager_->RunFileDialog(mode, title, default_file_path, |
| accept_filters, callback); |
| } |
| |
| void CefBrowserHostBase::StartDownload(const CefString& url) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, base::BindOnce(&CefBrowserHostBase::StartDownload, this, url)); |
| return; |
| } |
| |
| GURL gurl = GURL(url.ToString()); |
| if (gurl.is_empty() || !gurl.is_valid()) { |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| auto browser_context = web_contents->GetBrowserContext(); |
| if (!browser_context) { |
| return; |
| } |
| |
| content::DownloadManager* manager = browser_context->GetDownloadManager(); |
| if (!manager) { |
| return; |
| } |
| |
| std::unique_ptr<download::DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, gurl, MISSING_TRAFFIC_ANNOTATION)); |
| manager->DownloadUrl(std::move(params)); |
| } |
| |
| void CefBrowserHostBase::DownloadImage( |
| const CefString& image_url, |
| bool is_favicon, |
| uint32_t max_image_size, |
| bool bypass_cache, |
| CefRefPtr<CefDownloadImageCallback> callback) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::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; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| const float scale = ui::GetScaleForMaxSupportedResourceScaleFactor(); |
| web_contents->DownloadImage( |
| gurl, is_favicon, gfx::Size(max_image_size, max_image_size), |
| max_image_size * scale, bypass_cache, |
| base::BindOnce( |
| [](uint32_t max_image_size, |
| CefRefPtr<CefDownloadImageCallback> callback, int id, |
| int http_status_code, const GURL& image_url, |
| const std::vector<SkBitmap>& bitmaps, |
| const std::vector<gfx::Size>& sizes) { |
| CEF_REQUIRE_UIT(); |
| |
| CefRefPtr<CefImageImpl> 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()); |
| }, |
| max_image_size, callback)); |
| } |
| |
| void CefBrowserHostBase::Print() { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::Print, this)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| const bool print_preview_disabled = |
| !platform_delegate_ || !platform_delegate_->IsPrintPreviewSupported(); |
| print_util::Print(web_contents, print_preview_disabled); |
| } |
| |
| void CefBrowserHostBase::PrintToPDF(const CefString& path, |
| const CefPdfPrintSettings& settings, |
| CefRefPtr<CefPdfPrintCallback> callback) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::PrintToPDF, this, |
| path, settings, callback)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| print_util::PrintToPDF(web_contents, path, settings, callback); |
| } |
| |
| void CefBrowserHostBase::ShowDevTools(const CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient> client, |
| const CefBrowserSettings& settings, |
| const CefPoint& inspect_element_at) { |
| auto params = std::make_unique<CefShowDevToolsParams>( |
| windowInfo, client, settings, inspect_element_at); |
| |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::ShowDevToolsOnUIThread, |
| this, std::move(params))); |
| } else { |
| ShowDevToolsOnUIThread(std::move(params)); |
| } |
| } |
| |
| bool CefBrowserHostBase::SendDevToolsMessage(const void* message, |
| size_t message_size) { |
| if (!message || message_size == 0) { |
| return false; |
| } |
| |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| std::string message_str(static_cast<const char*>(message), message_size); |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce( |
| [](CefRefPtr<CefBrowserHostBase> self, std::string message_str) { |
| self->SendDevToolsMessage(message_str.data(), message_str.size()); |
| }, |
| CefRefPtr<CefBrowserHostBase>(this), std::move(message_str))); |
| return false; |
| } |
| |
| if (!EnsureDevToolsManager()) { |
| return false; |
| } |
| return devtools_manager_->SendDevToolsMessage(message, message_size); |
| } |
| |
| int CefBrowserHostBase::ExecuteDevToolsMethod( |
| int message_id, |
| const CefString& method, |
| CefRefPtr<CefDictionaryValue> params) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, base::BindOnce(base::IgnoreResult( |
| &CefBrowserHostBase::ExecuteDevToolsMethod), |
| this, message_id, method, params)); |
| return 0; |
| } |
| |
| if (!EnsureDevToolsManager()) { |
| return 0; |
| } |
| return devtools_manager_->ExecuteDevToolsMethod(message_id, method, params); |
| } |
| |
| CefRefPtr<CefRegistration> CefBrowserHostBase::AddDevToolsMessageObserver( |
| CefRefPtr<CefDevToolsMessageObserver> observer) { |
| if (!observer) { |
| return nullptr; |
| } |
| auto registration = CefDevToolsManager::CreateRegistration(observer); |
| InitializeDevToolsRegistrationOnUIThread(registration); |
| return registration.get(); |
| } |
| |
| void CefBrowserHostBase::GetNavigationEntries( |
| CefRefPtr<CefNavigationEntryVisitor> visitor, |
| bool current_only) { |
| DCHECK(visitor.get()); |
| if (!visitor.get()) { |
| return; |
| } |
| |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, base::BindOnce(&CefBrowserHostBase::GetNavigationEntries, this, |
| visitor, current_only)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| 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<CefNavigationEntryImpl> entry = |
| new CefNavigationEntryImpl(controller.GetEntryAtIndex(current)); |
| visitor->Visit(entry.get(), true, current, total); |
| std::ignore = entry->Detach(nullptr); |
| } else { |
| // Visit all entries. |
| bool cont = true; |
| for (int i = 0; i < total && cont; ++i) { |
| CefRefPtr<CefNavigationEntryImpl> entry = |
| new CefNavigationEntryImpl(controller.GetEntryAtIndex(i)); |
| cont = visitor->Visit(entry.get(), (i == current), i, total); |
| std::ignore = entry->Detach(nullptr); |
| } |
| } |
| } |
| |
| CefRefPtr<CefNavigationEntry> CefBrowserHostBase::GetVisibleNavigationEntry() { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| DCHECK(false) << "called on invalid thread"; |
| return nullptr; |
| } |
| |
| content::NavigationEntry* entry = nullptr; |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| entry = web_contents->GetController().GetVisibleEntry(); |
| } |
| |
| if (!entry) { |
| return nullptr; |
| } |
| |
| return new CefNavigationEntryImpl(entry); |
| } |
| |
| void CefBrowserHostBase::NotifyMoveOrResizeStarted() { |
| #if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)) |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::NotifyMoveOrResizeStarted, this)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->NotifyMoveOrResizeStarted(); |
| } |
| #endif |
| } |
| |
| bool CefBrowserHostBase::IsFullscreen() { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| DCHECK(false) << "called on invalid thread"; |
| return false; |
| } |
| |
| if (auto web_contents = GetWebContents()) { |
| return web_contents->IsFullscreen(); |
| } |
| return false; |
| } |
| |
| void CefBrowserHostBase::ExitFullscreen(bool will_cause_resize) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::ExitFullscreen, |
| this, will_cause_resize)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (web_contents && web_contents->IsFullscreen()) { |
| web_contents->ExitFullscreen(will_cause_resize); |
| } |
| } |
| |
| void CefBrowserHostBase::ReplaceMisspelling(const CefString& word) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::ReplaceMisspelling, this, word)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| web_contents->ReplaceMisspelling(word); |
| } |
| } |
| |
| void CefBrowserHostBase::AddWordToDictionary(const CefString& word) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::AddWordToDictionary, this, word)); |
| return; |
| } |
| |
| auto web_contents = GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| SpellcheckService* spellcheck = nullptr; |
| content::BrowserContext* browser_context = web_contents->GetBrowserContext(); |
| if (browser_context) { |
| spellcheck = SpellcheckServiceFactory::GetForContext(browser_context); |
| if (spellcheck) { |
| spellcheck->GetCustomDictionary()->AddWord(word); |
| } |
| } |
| #if BUILDFLAG(IS_MAC) |
| if (spellcheck && spellcheck::UseBrowserSpellChecker()) { |
| spellcheck_platform::AddWord(spellcheck->platform_spell_checker(), word); |
| } |
| #endif |
| } |
| |
| void CefBrowserHostBase::SendKeyEvent(const CefKeyEvent& event) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::SendKeyEvent, |
| this, event)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->SendKeyEvent(event); |
| } |
| } |
| |
| void CefBrowserHostBase::SendMouseClickEvent(const CefMouseEvent& event, |
| MouseButtonType type, |
| bool mouseUp, |
| int clickCount) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::SendMouseClickEvent, this, |
| event, type, mouseUp, clickCount)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->SendMouseClickEvent(event, type, mouseUp, clickCount); |
| } |
| } |
| |
| void CefBrowserHostBase::SendMouseMoveEvent(const CefMouseEvent& event, |
| bool mouseLeave) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::SendMouseMoveEvent, this, |
| event, mouseLeave)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->SendMouseMoveEvent(event, mouseLeave); |
| } |
| } |
| |
| void CefBrowserHostBase::SendMouseWheelEvent(const CefMouseEvent& event, |
| int deltaX, |
| int deltaY) { |
| if (deltaX == 0 && deltaY == 0) { |
| // Nothing to do. |
| return; |
| } |
| |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::SendMouseWheelEvent, this, |
| event, deltaX, deltaY)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->SendMouseWheelEvent(event, deltaX, deltaY); |
| } |
| } |
| |
| bool CefBrowserHostBase::IsValid() { |
| return browser_info_->browser() == this; |
| } |
| |
| CefRefPtr<CefBrowserHost> CefBrowserHostBase::GetHost() { |
| return this; |
| } |
| |
| bool CefBrowserHostBase::CanGoBack() { |
| base::AutoLock lock_scope(state_lock_); |
| return can_go_back_; |
| } |
| |
| void CefBrowserHostBase::GoBack() { |
| auto callback = base::BindOnce(&CefBrowserHostBase::GoBack, this); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| auto wc = GetWebContents(); |
| if (wc && wc->GetController().CanGoBack()) { |
| wc->GetController().GoBack(); |
| } |
| } |
| |
| bool CefBrowserHostBase::CanGoForward() { |
| base::AutoLock lock_scope(state_lock_); |
| return can_go_forward_; |
| } |
| |
| void CefBrowserHostBase::GoForward() { |
| auto callback = base::BindOnce(&CefBrowserHostBase::GoForward, this); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| auto wc = GetWebContents(); |
| if (wc && wc->GetController().CanGoForward()) { |
| wc->GetController().GoForward(); |
| } |
| } |
| |
| bool CefBrowserHostBase::IsLoading() { |
| base::AutoLock lock_scope(state_lock_); |
| return is_loading_; |
| } |
| |
| void CefBrowserHostBase::Reload() { |
| auto callback = base::BindOnce(&CefBrowserHostBase::Reload, this); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| auto wc = GetWebContents(); |
| if (wc) { |
| wc->GetController().Reload(content::ReloadType::NORMAL, true); |
| } |
| } |
| |
| void CefBrowserHostBase::ReloadIgnoreCache() { |
| auto callback = base::BindOnce(&CefBrowserHostBase::ReloadIgnoreCache, this); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| auto wc = GetWebContents(); |
| if (wc) { |
| wc->GetController().Reload(content::ReloadType::BYPASSING_CACHE, true); |
| } |
| } |
| |
| void CefBrowserHostBase::StopLoad() { |
| auto callback = base::BindOnce(&CefBrowserHostBase::StopLoad, this); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| auto wc = GetWebContents(); |
| if (wc) { |
| wc->Stop(); |
| } |
| } |
| |
| int CefBrowserHostBase::GetIdentifier() { |
| return browser_id(); |
| } |
| |
| bool CefBrowserHostBase::IsSame(CefRefPtr<CefBrowser> that) { |
| auto impl = static_cast<CefBrowserHostBase*>(that.get()); |
| return (impl == this); |
| } |
| |
| bool CefBrowserHostBase::HasDocument() { |
| base::AutoLock lock_scope(state_lock_); |
| return has_document_; |
| } |
| |
| bool CefBrowserHostBase::IsPopup() { |
| return browser_info_->is_popup(); |
| } |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetMainFrame() { |
| return GetFrame(CefFrameHostImpl::kMainFrameId); |
| } |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetFocusedFrame() { |
| return GetFrame(CefFrameHostImpl::kFocusedFrameId); |
| } |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetFrame(int64_t identifier) { |
| if (identifier == CefFrameHostImpl::kInvalidFrameId) { |
| return nullptr; |
| } else if (identifier == CefFrameHostImpl::kMainFrameId) { |
| return browser_info_->GetMainFrame(); |
| } else if (identifier == CefFrameHostImpl::kFocusedFrameId) { |
| base::AutoLock lock_scope(state_lock_); |
| if (!focused_frame_) { |
| // The main frame is focused by default. |
| return browser_info_->GetMainFrame(); |
| } |
| return focused_frame_; |
| } |
| |
| return browser_info_->GetFrameForGlobalId( |
| frame_util::MakeGlobalId(identifier)); |
| } |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetFrame(const CefString& name) { |
| for (const auto& frame : browser_info_->GetAllFrames()) { |
| if (frame->GetName() == name) { |
| return frame; |
| } |
| } |
| return nullptr; |
| } |
| |
| size_t CefBrowserHostBase::GetFrameCount() { |
| return browser_info_->GetAllFrames().size(); |
| } |
| |
| void CefBrowserHostBase::GetFrameIdentifiers( |
| std::vector<int64_t>& identifiers) { |
| if (identifiers.size() > 0) { |
| identifiers.clear(); |
| } |
| |
| const auto frames = browser_info_->GetAllFrames(); |
| if (frames.empty()) { |
| return; |
| } |
| |
| identifiers.reserve(frames.size()); |
| for (const auto& frame : frames) { |
| identifiers.push_back(frame->GetIdentifier()); |
| } |
| } |
| |
| void CefBrowserHostBase::GetFrameNames(std::vector<CefString>& names) { |
| if (names.size() > 0) { |
| names.clear(); |
| } |
| |
| const auto frames = browser_info_->GetAllFrames(); |
| if (frames.empty()) { |
| return; |
| } |
| |
| names.reserve(frames.size()); |
| for (const auto& frame : frames) { |
| names.push_back(frame->GetName()); |
| } |
| } |
| |
| void CefBrowserHostBase::OnStateChanged(CefBrowserContentsState state_changed) { |
| // Make sure that CefBrowser state is consistent before the associated |
| // CefClient callback is executed. |
| base::AutoLock lock_scope(state_lock_); |
| if ((state_changed & CefBrowserContentsState::kNavigation) == |
| CefBrowserContentsState::kNavigation) { |
| is_loading_ = contents_delegate_->is_loading(); |
| can_go_back_ = contents_delegate_->can_go_back(); |
| can_go_forward_ = contents_delegate_->can_go_forward(); |
| } |
| if ((state_changed & CefBrowserContentsState::kDocument) == |
| CefBrowserContentsState::kDocument) { |
| has_document_ = contents_delegate_->has_document(); |
| } |
| if ((state_changed & CefBrowserContentsState::kFullscreen) == |
| CefBrowserContentsState::kFullscreen) { |
| is_fullscreen_ = contents_delegate_->is_fullscreen(); |
| } |
| if ((state_changed & CefBrowserContentsState::kFocusedFrame) == |
| CefBrowserContentsState::kFocusedFrame) { |
| focused_frame_ = contents_delegate_->focused_frame(); |
| } |
| } |
| |
| void CefBrowserHostBase::OnWebContentsDestroyed( |
| content::WebContents* web_contents) {} |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetFrameForHost( |
| const content::RenderFrameHost* host) { |
| CEF_REQUIRE_UIT(); |
| if (!host) { |
| return nullptr; |
| } |
| |
| return browser_info_->GetFrameForHost(host); |
| } |
| |
| CefRefPtr<CefFrame> CefBrowserHostBase::GetFrameForGlobalId( |
| const content::GlobalRenderFrameHostId& global_id) { |
| return browser_info_->GetFrameForGlobalId(global_id, nullptr); |
| } |
| |
| void CefBrowserHostBase::AddObserver(Observer* observer) { |
| CEF_REQUIRE_UIT(); |
| observers_.AddObserver(observer); |
| } |
| |
| void CefBrowserHostBase::RemoveObserver(Observer* observer) { |
| CEF_REQUIRE_UIT(); |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool CefBrowserHostBase::HasObserver(Observer* observer) const { |
| CEF_REQUIRE_UIT(); |
| return observers_.HasObserver(observer); |
| } |
| |
| void CefBrowserHostBase::LoadMainFrameURL( |
| const content::OpenURLParams& params) { |
| auto callback = |
| base::BindOnce(&CefBrowserHostBase::LoadMainFrameURL, this, params); |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, std::move(callback)); |
| return; |
| } |
| |
| if (browser_info_->IsNavigationLocked(std::move(callback))) { |
| return; |
| } |
| |
| if (Navigate(params)) { |
| OnSetFocus(FOCUS_SOURCE_NAVIGATION); |
| } |
| } |
| |
| bool CefBrowserHostBase::Navigate(const content::OpenURLParams& params) { |
| CEF_REQUIRE_UIT(); |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| GURL gurl = params.url; |
| if (!url_util::FixupGURL(gurl)) { |
| return false; |
| } |
| |
| web_contents->GetController().LoadURL( |
| gurl, params.referrer, params.transition, params.extra_headers); |
| return true; |
| } |
| return false; |
| } |
| |
| void CefBrowserHostBase::ViewText(const std::string& text) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK(CEF_UIT, |
| base::BindOnce(&CefBrowserHostBase::ViewText, this, text)); |
| return; |
| } |
| |
| if (platform_delegate_) { |
| platform_delegate_->ViewText(text); |
| } |
| } |
| |
| void CefBrowserHostBase::RunFileChooserForBrowser( |
| const blink::mojom::FileChooserParams& params, |
| CefFileDialogManager::RunFileChooserCallback callback) { |
| if (!EnsureFileDialogManager()) { |
| LOG(ERROR) << "File dialog canceled due to invalid state."; |
| std::move(callback).Run({}); |
| return; |
| } |
| file_dialog_manager_->RunFileChooser(params, std::move(callback)); |
| } |
| |
| void CefBrowserHostBase::RunSelectFile( |
| ui::SelectFileDialog::Listener* listener, |
| std::unique_ptr<ui::SelectFilePolicy> policy, |
| ui::SelectFileDialog::Type type, |
| const std::u16string& title, |
| const base::FilePath& default_path, |
| const ui::SelectFileDialog::FileTypeInfo* file_types, |
| int file_type_index, |
| const base::FilePath::StringType& default_extension, |
| gfx::NativeWindow owning_window, |
| void* params) { |
| if (!EnsureFileDialogManager()) { |
| LOG(ERROR) << "File dialog canceled due to invalid state."; |
| listener->FileSelectionCanceled(params); |
| return; |
| } |
| file_dialog_manager_->RunSelectFile(listener, std::move(policy), type, title, |
| default_path, file_types, file_type_index, |
| default_extension, owning_window, params); |
| } |
| |
| void CefBrowserHostBase::SelectFileListenerDestroyed( |
| ui::SelectFileDialog::Listener* listener) { |
| if (file_dialog_manager_) { |
| file_dialog_manager_->SelectFileListenerDestroyed(listener); |
| } |
| } |
| |
| bool CefBrowserHostBase::MaybeAllowNavigation( |
| content::RenderFrameHost* opener, |
| bool is_guest_view, |
| const content::OpenURLParams& params) { |
| return true; |
| } |
| |
| void CefBrowserHostBase::OnAfterCreated() { |
| CEF_REQUIRE_UIT(); |
| if (client_) { |
| if (auto handler = client_->GetLifeSpanHandler()) { |
| handler->OnAfterCreated(this); |
| } |
| } |
| } |
| |
| void CefBrowserHostBase::OnBeforeClose() { |
| CEF_REQUIRE_UIT(); |
| if (client_) { |
| if (auto handler = client_->GetLifeSpanHandler()) { |
| handler->OnBeforeClose(this); |
| } |
| } |
| browser_info_->SetClosing(); |
| } |
| |
| void CefBrowserHostBase::OnBrowserDestroyed() { |
| CEF_REQUIRE_UIT(); |
| |
| // Destroy any platform constructs. |
| if (file_dialog_manager_) { |
| file_dialog_manager_->Destroy(); |
| file_dialog_manager_.reset(); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.OnBrowserDestroyed(this); |
| } |
| } |
| |
| int CefBrowserHostBase::browser_id() const { |
| return browser_info_->browser_id(); |
| } |
| |
| SkColor CefBrowserHostBase::GetBackgroundColor() const { |
| // Don't use |platform_delegate_| because it's not thread-safe. |
| return CefContext::Get()->GetBackgroundColor( |
| &settings_, IsWindowless() ? STATE_ENABLED : STATE_DISABLED); |
| } |
| |
| bool CefBrowserHostBase::IsWindowless() const { |
| return false; |
| } |
| |
| content::WebContents* CefBrowserHostBase::GetWebContents() const { |
| CEF_REQUIRE_UIT(); |
| return contents_delegate_->web_contents(); |
| } |
| |
| content::BrowserContext* CefBrowserHostBase::GetBrowserContext() const { |
| CEF_REQUIRE_UIT(); |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| return web_contents->GetBrowserContext(); |
| } |
| return nullptr; |
| } |
| |
| CefMediaStreamRegistrar* CefBrowserHostBase::GetMediaStreamRegistrar() { |
| CEF_REQUIRE_UIT(); |
| if (!media_stream_registrar_) { |
| media_stream_registrar_ = std::make_unique<CefMediaStreamRegistrar>(this); |
| } |
| return media_stream_registrar_.get(); |
| } |
| |
| views::Widget* CefBrowserHostBase::GetWindowWidget() const { |
| CEF_REQUIRE_UIT(); |
| if (!platform_delegate_) { |
| return nullptr; |
| } |
| return platform_delegate_->GetWindowWidget(); |
| } |
| |
| CefRefPtr<CefBrowserView> CefBrowserHostBase::GetBrowserView() const { |
| CEF_REQUIRE_UIT(); |
| if (is_views_hosted_ && platform_delegate_) { |
| return platform_delegate_->GetBrowserView(); |
| } |
| return nullptr; |
| } |
| |
| gfx::NativeWindow CefBrowserHostBase::GetTopLevelNativeWindow() const { |
| CEF_REQUIRE_UIT(); |
| // Windowless browsers always return nullptr from GetTopLevelNativeWindow(). |
| if (!IsWindowless()) { |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| return web_contents->GetTopLevelNativeWindow(); |
| } |
| } |
| return gfx::NativeWindow(); |
| } |
| |
| bool CefBrowserHostBase::IsFocused() const { |
| CEF_REQUIRE_UIT(); |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| return static_cast<content::RenderFrameHostImpl*>( |
| web_contents->GetPrimaryMainFrame()) |
| ->IsFocused(); |
| } |
| return false; |
| } |
| |
| bool CefBrowserHostBase::IsVisible() const { |
| CEF_REQUIRE_UIT(); |
| // Windowless browsers always return nullptr from GetNativeView(). |
| if (!IsWindowless()) { |
| auto web_contents = GetWebContents(); |
| if (web_contents) { |
| return platform_util::IsVisible(web_contents->GetNativeView()); |
| } |
| } |
| return false; |
| } |
| |
| bool CefBrowserHostBase::EnsureDevToolsManager() { |
| CEF_REQUIRE_UIT(); |
| if (!contents_delegate_->web_contents()) { |
| return false; |
| } |
| |
| if (!devtools_manager_) { |
| devtools_manager_ = std::make_unique<CefDevToolsManager>(this); |
| } |
| return true; |
| } |
| |
| void CefBrowserHostBase::InitializeDevToolsRegistrationOnUIThread( |
| CefRefPtr<CefRegistration> registration) { |
| if (!CEF_CURRENTLY_ON_UIT()) { |
| CEF_POST_TASK( |
| CEF_UIT, |
| base::BindOnce( |
| &CefBrowserHostBase::InitializeDevToolsRegistrationOnUIThread, this, |
| registration)); |
| return; |
| } |
| |
| if (!EnsureDevToolsManager()) { |
| return; |
| } |
| devtools_manager_->InitializeRegistrationOnUIThread(registration); |
| } |
| |
| bool CefBrowserHostBase::EnsureFileDialogManager() { |
| CEF_REQUIRE_UIT(); |
| if (!contents_delegate_->web_contents()) { |
| return false; |
| } |
| |
| if (!file_dialog_manager_) { |
| file_dialog_manager_ = std::make_unique<CefFileDialogManager>(this); |
| } |
| return true; |
| } |