// Copyright 2018 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 "content/browser/content_service_delegate_impl.h" #include "base/macros.h" #include "base/optional.h" #include "content/public/browser/focused_node_details.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "services/content/navigable_contents_delegate.h" #include "services/content/service.h" #include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "third_party/skia/include/core/SkColor.h" namespace content { namespace { // Bridge between Content Service navigable contents delegation API and a // WebContentsImpl. class NavigableContentsDelegateImpl : public content::NavigableContentsDelegate, public WebContentsDelegate, public WebContentsObserver { public: explicit NavigableContentsDelegateImpl( BrowserContext* browser_context, const mojom::NavigableContentsParams& params, mojom::NavigableContentsClient* client) : client_(client), enable_view_auto_resize_(params.enable_view_auto_resize), auto_resize_min_size_( params.auto_resize_min_size.value_or(gfx::Size(1, 1))), auto_resize_max_size_( params.auto_resize_max_size.value_or(gfx::Size(INT_MAX, INT_MAX))), background_color_(params.override_background_color ? base::make_optional(params.background_color) : base::nullopt) { WebContents::CreateParams create_params(browser_context); web_contents_ = WebContents::Create(create_params); WebContentsObserver::Observe(web_contents_.get()); web_contents_->SetDelegate(this); blink::mojom::RendererPreferences* renderer_prefs = web_contents_->GetMutableRendererPrefs(); renderer_prefs->can_accept_load_drops = false; renderer_prefs->browser_handles_all_top_level_requests = params.suppress_navigations; web_contents_->SyncRendererPrefs(); } ~NavigableContentsDelegateImpl() override { WebContentsObserver::Observe(nullptr); } bool TakeFocus(WebContents* source, bool reverse) override { client_->ClearViewFocus(); return true; } private: void NotifyAXTreeChange() { auto* rfh = web_contents_->GetMainFrame(); if (rfh) client_->UpdateContentAXTree(rfh->GetAXTreeID()); else client_->UpdateContentAXTree(ui::AXTreeIDUnknown()); } // content::NavigableContentsDelegate: gfx::NativeView GetNativeView() override { return web_contents_->GetNativeView(); } void Navigate(const GURL& url, content::mojom::NavigateParamsPtr params) override { NavigationController::LoadURLParams load_url_params(url); load_url_params.transition_type = ui::PAGE_TRANSITION_AUTO_TOPLEVEL; load_url_params.should_clear_history_list = params->should_clear_session_history; web_contents_->GetController().LoadURLWithParams(load_url_params); } void GoBack( content::mojom::NavigableContents::GoBackCallback callback) override { content::NavigationController& controller = web_contents_->GetController(); if (controller.CanGoBack()) { std::move(callback).Run(/*success=*/true); controller.GoBack(); } else { std::move(callback).Run(/*success=*/false); } } void Focus() override { web_contents_->Focus(); } void FocusThroughTabTraversal(bool reverse) override { web_contents_->FocusThroughTabTraversal(reverse); } // WebContentsDelegate: bool IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, const GURL& opener_url, const std::string& frame_name, const GURL& target_url) override { // This method is invoked when attempting to open links in a new tab, e.g.: // Link client_->DidSuppressNavigation(target_url, WindowOpenDisposition::NEW_FOREGROUND_TAB, /*from_user_gesture=*/true); return true; } WebContents* OpenURLFromTab(WebContents* source, const OpenURLParams& params) override { client_->DidSuppressNavigation(params.url, params.disposition, params.user_gesture); return nullptr; } void ResizeDueToAutoResize(WebContents* web_contents, const gfx::Size& new_size) override { DCHECK_EQ(web_contents, web_contents_.get()); client_->DidAutoResizeView(new_size); } void NavigationStateChanged(WebContents* source, InvalidateTypes changed_flags) override { MaybeNotifyCanGoBack(); } // WebContentsObserver: void RenderViewReady() override { if (background_color_) { web_contents_->GetRenderViewHost() ->GetWidget() ->GetView() ->SetBackgroundColor(background_color_.value()); } } void RenderViewCreated(RenderViewHost* render_view_host) override { if (background_color_) { render_view_host->GetWidget()->GetView()->SetBackgroundColor( background_color_.value()); } } void RenderViewHostChanged(RenderViewHost* old_host, RenderViewHost* new_host) override { if (enable_view_auto_resize_ && web_contents_->GetRenderWidgetHostView()) { web_contents_->GetRenderWidgetHostView()->EnableAutoResize( auto_resize_min_size_, auto_resize_max_size_); } if (background_color_) { new_host->GetWidget()->GetView()->SetBackgroundColor( background_color_.value()); } NotifyAXTreeChange(); } void DidFinishNavigation(NavigationHandle* navigation_handle) override { client_->DidFinishNavigation( navigation_handle->GetURL(), navigation_handle->IsInMainFrame(), navigation_handle->IsErrorPage(), navigation_handle->GetResponseHeaders() ? base::MakeRefCounted( navigation_handle->GetResponseHeaders()->raw_headers()) : nullptr); } void DidStopLoading() override { client_->DidStopLoading(); } void NavigationEntriesDeleted() override { MaybeNotifyCanGoBack(); } // Notifies the client whether the web contents can navigate back in its // history stack. void MaybeNotifyCanGoBack() { const bool can_go_back = web_contents_->GetController().CanGoBack(); if (can_go_back_ == can_go_back) return; can_go_back_ = can_go_back; client_->UpdateCanGoBack(can_go_back); } void OnFocusChangedInPage(FocusedNodeDetails* details) override { client_->FocusedNodeChanged(details->is_editable_node, details->node_bounds_in_screen); } std::unique_ptr web_contents_; mojom::NavigableContentsClient* const client_; const bool enable_view_auto_resize_; const gfx::Size auto_resize_min_size_; const gfx::Size auto_resize_max_size_; const base::Optional background_color_; bool can_go_back_ = false; DISALLOW_COPY_AND_ASSIGN(NavigableContentsDelegateImpl); }; } // namespace ContentServiceDelegateImpl::ContentServiceDelegateImpl( BrowserContext* browser_context) : browser_context_(browser_context) {} ContentServiceDelegateImpl::~ContentServiceDelegateImpl() { // This delegate is destroyed immediately before |browser_context_| is // destroyed. We force-kill any Content Service instances which depend on // |this|, since they will no longer be functional anyway. std::set instances; std::swap(instances, service_instances_); for (content::Service* service : instances) { // Eventually destroys |service|. Ensures that no more calls into |this| // will occur. service->ForceQuit(); } } void ContentServiceDelegateImpl::AddService(content::Service* service) { service_instances_.insert(service); } void ContentServiceDelegateImpl::WillDestroyServiceInstance( content::Service* service) { service_instances_.erase(service); } std::unique_ptr ContentServiceDelegateImpl::CreateNavigableContentsDelegate( const mojom::NavigableContentsParams& params, mojom::NavigableContentsClient* client) { return std::make_unique(browser_context_, params, client); } } // namespace content