// Copyright 2015 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/alloy/browser_platform_delegate_alloy.h" #include "libcef/browser/alloy/alloy_browser_host_impl.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/printing/print_view_manager.h" #include "libcef/common/extensions/extensions_util.h" #include "libcef/common/net/url_util.h" #include "libcef/features/runtime_checks.h" #include "base/logging.h" #include "chrome/browser/printing/print_view_manager.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "components/zoom/zoom_controller.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "extensions/browser/process_manager.h" #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" namespace { printing::CefPrintViewManager* GetPrintViewManager( content::WebContents* web_contents) { return printing::CefPrintViewManager::FromWebContents(web_contents); } } // namespace CefBrowserPlatformDelegateAlloy::CefBrowserPlatformDelegateAlloy() : weak_ptr_factory_(this) {} content::WebContents* CefBrowserPlatformDelegateAlloy::CreateWebContents( CefBrowserCreateParams& create_params, bool& own_web_contents) { REQUIRE_ALLOY_RUNTIME(); DCHECK(primary_); // Get or create the request context and browser context. CefRefPtr request_context_impl = CefRequestContextImpl::GetOrCreateForRequestContext( create_params.request_context); CHECK(request_context_impl); auto cef_browser_context = request_context_impl->GetBrowserContext(); CHECK(cef_browser_context); auto browser_context = cef_browser_context->AsBrowserContext(); if (!create_params.request_context) { // Using the global request context. create_params.request_context = request_context_impl.get(); } scoped_refptr site_instance; if (extensions::ExtensionsEnabled() && !create_params.url.empty()) { GURL gurl = url_util::MakeGURL(create_params.url, /*fixup=*/true); 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, gurl); } if (create_params.extension) { if (create_params.extension_host_type == extensions::mojom::ViewType::kInvalid) { // Default to dialog behavior. create_params.extension_host_type = extensions::mojom::ViewType::kExtensionDialog; } // Extension resources will fail to load if we don't use a SiteInstance // associated with the extension. // (AlloyContentBrowserClient::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(gurl); DCHECK(site_instance); } } content::WebContents::CreateParams wc_create_params(browser_context, site_instance); if (IsWindowless()) { // Create the OSR view for the WebContents. CreateViewForWebContents(&wc_create_params.view, &wc_create_params.delegate_view); } auto web_contents = content::WebContents::Create(wc_create_params); CHECK(web_contents); own_web_contents = true; return web_contents.release(); } void CefBrowserPlatformDelegateAlloy::WebContentsCreated( content::WebContents* web_contents, bool owned) { CefBrowserPlatformDelegate::WebContentsCreated(web_contents, owned); if (owned) { SetOwnedWebContents(web_contents); } } void CefBrowserPlatformDelegateAlloy::AddNewContents( content::WebContents* source, std::unique_ptr new_contents, const GURL& target_url, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { REQUIRE_ALLOY_RUNTIME(); DCHECK(primary_); CefRefPtr owner = AlloyBrowserHostImpl::GetBrowserForContents(new_contents.get()); if (owner) { // Taking ownership of |new_contents|. static_cast( owner->platform_delegate_.get()) ->SetOwnedWebContents(new_contents.release()); return; } if (extension_host_) { extension_host_->AddNewContents(source, std::move(new_contents), target_url, disposition, initial_rect, user_gesture, was_blocked); } } bool CefBrowserPlatformDelegateAlloy::ShouldTransferNavigation( bool is_main_frame_navigation) { if (extension_host_) { return extension_host_->ShouldTransferNavigation(is_main_frame_navigation); } return true; } void CefBrowserPlatformDelegateAlloy::RenderViewCreated( content::RenderViewHost* render_view_host) { // Indicate that the view has an external parent (namely us). This changes the // default view behavior in some cases (e.g. focus handling on Linux). if (!IsViewsHosted() && render_view_host->GetWidget()->GetView()) render_view_host->GetWidget()->GetView()->SetHasExternalParent(true); } void CefBrowserPlatformDelegateAlloy::RenderViewReady() { ConfigureAutoResize(); } void CefBrowserPlatformDelegateAlloy::BrowserCreated( CefBrowserHostBase* browser) { CefBrowserPlatformDelegate::BrowserCreated(browser); // Only register WebContents delegate/observers if we're the primary delegate. if (!primary_) return; DCHECK(!web_contents_->GetDelegate()); web_contents_->SetDelegate(static_cast(browser)); 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_); } if (IsPrintPreviewSupported()) { web_contents_dialog_helper_.reset( new CefWebContentsDialogHelper(web_contents_, this)); } } void CefBrowserPlatformDelegateAlloy::CreateExtensionHost( const extensions::Extension* extension, const GURL& url, extensions::mojom::ViewType host_type) { REQUIRE_ALLOY_RUNTIME(); DCHECK(primary_); // Should get WebContentsCreated and BrowserCreated calls first. DCHECK(web_contents_); DCHECK(browser_); DCHECK(!extension_host_); auto alloy_browser = static_cast(browser_); if (host_type == extensions::mojom::ViewType::kExtensionDialog || host_type == extensions::mojom::ViewType::kExtensionPopup) { // Create an extension host that we own. extension_host_ = new extensions::CefExtensionViewHost( alloy_browser, extension, web_contents_, url, host_type); // Trigger load of the extension URL. extension_host_->CreateRendererSoon(); } else if (host_type == extensions::mojom::ViewType::kExtensionBackgroundPage) { is_background_host_ = true; alloy_browser->is_background_host_ = true; // Create an extension host that will be owned by ProcessManager. extension_host_ = new extensions::CefExtensionBackgroundHost( alloy_browser, base::BindOnce(&CefBrowserPlatformDelegateAlloy::OnExtensionHostDeleted, weak_ptr_factory_.GetWeakPtr()), extension, web_contents_, url, host_type); // Load will be triggered by ProcessManager::CreateBackgroundHost. } else { NOTREACHED() << " Unsupported extension host type: " << host_type; } } extensions::ExtensionHost* CefBrowserPlatformDelegateAlloy::GetExtensionHost() const { return extension_host_; } void CefBrowserPlatformDelegateAlloy::BrowserDestroyed( CefBrowserHostBase* browser) { if (primary_) { DestroyExtensionHost(); owned_web_contents_.reset(); } CefBrowserPlatformDelegate::BrowserDestroyed(browser); } void CefBrowserPlatformDelegateAlloy::SendCaptureLostEvent() { if (!web_contents_) return; content::RenderViewHost* host = web_contents_->GetRenderViewHost(); if (!host) return; content::RenderWidgetHostImpl* widget = content::RenderWidgetHostImpl::From(host->GetWidget()); if (widget) widget->LostCapture(); } #if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MAC)) void CefBrowserPlatformDelegateAlloy::NotifyMoveOrResizeStarted() { if (!browser_) return; // Dismiss any existing popups. auto frame = browser_->GetMainFrame(); if (frame && frame->IsValid()) { static_cast(frame.get())->NotifyMoveOrResizeStarted(); } } #endif bool CefBrowserPlatformDelegateAlloy::PreHandleGestureEvent( content::WebContents* source, const blink::WebGestureEvent& event) { if (extension_host_) return extension_host_->PreHandleGestureEvent(source, event); return false; } bool CefBrowserPlatformDelegateAlloy::IsNeverComposited( content::WebContents* web_contents) { if (extension_host_) return extension_host_->IsNeverComposited(web_contents); return false; } void CefBrowserPlatformDelegateAlloy::SetAutoResizeEnabled( bool enabled, const CefSize& min_size, const CefSize& max_size) { 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(); } void CefBrowserPlatformDelegateAlloy::ConfigureAutoResize() { 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()); } } void CefBrowserPlatformDelegateAlloy::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; 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); } bool CefBrowserPlatformDelegateAlloy::IsPrintPreviewSupported() const { REQUIRE_ALLOY_RUNTIME(); auto actionable_contents = GetActionableWebContents(); if (!actionable_contents) return false; auto cef_browser_context = CefBrowserContext::FromBrowserContext( actionable_contents->GetBrowserContext()); if (!cef_browser_context->IsPrintPreviewSupported()) { return false; } // Print preview is not currently supported with OSR. return !IsWindowless(); } void CefBrowserPlatformDelegateAlloy::Print() { REQUIRE_ALLOY_RUNTIME(); auto actionable_contents = GetActionableWebContents(); if (!actionable_contents) return; auto rfh = actionable_contents->GetMainFrame(); if (IsPrintPreviewSupported()) { GetPrintViewManager(actionable_contents)->PrintPreviewNow(rfh, false); } else { GetPrintViewManager(actionable_contents)->PrintNow(rfh); } } void CefBrowserPlatformDelegateAlloy::PrintToPDF( const CefString& path, const CefPdfPrintSettings& settings, CefRefPtr callback) { REQUIRE_ALLOY_RUNTIME(); 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); } GetPrintViewManager(actionable_contents) ->PrintToPDF(actionable_contents->GetMainFrame(), base::FilePath(path), settings, pdf_callback); } void CefBrowserPlatformDelegateAlloy::Find(int identifier, const CefString& searchText, bool forward, bool matchCase, bool findNext) { 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; auto options = blink::mojom::FindOptions::New(); options->forward = forward; options->match_case = matchCase; options->find_match = findNext; web_contents_->Find(identifier, searchText, std::move(options)); } void CefBrowserPlatformDelegateAlloy::StopFinding(bool clearSelection) { if (!web_contents_) return; content::StopFindAction action = clearSelection ? content::STOP_FIND_ACTION_CLEAR_SELECTION : content::STOP_FIND_ACTION_KEEP_SELECTION; web_contents_->StopFinding(action); } base::RepeatingClosure CefBrowserPlatformDelegateAlloy::GetBoundsChangedCallback() { if (web_contents_dialog_helper_) { return web_contents_dialog_helper_->GetBoundsChangedCallback(); } return base::RepeatingClosure(); } content::WebContents* CefBrowserPlatformDelegateAlloy::GetActionableWebContents() const { if (web_contents_ && extensions::ExtensionsEnabled()) { content::WebContents* guest_contents = extensions::GetFullPageGuestForOwnerContents(web_contents_); if (guest_contents) return guest_contents; } return web_contents_; } void CefBrowserPlatformDelegateAlloy::SetOwnedWebContents( content::WebContents* owned_contents) { DCHECK(primary_); // Should not currently own a WebContents. CHECK(!owned_web_contents_); owned_web_contents_.reset(owned_contents); } void CefBrowserPlatformDelegateAlloy::DestroyExtensionHost() { if (!extension_host_) return; if (extension_host_->extension_host_type() == extensions::mojom::ViewType::kExtensionBackgroundPage) { 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 CefBrowserPlatformDelegateAlloy::OnExtensionHostDeleted() { DCHECK(is_background_host_); DCHECK(extension_host_); extension_host_ = nullptr; }