| // 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/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_->GetRenderViewHost()->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 ShouldCreateWebContents( |
| content::WebContents* web_contents, |
| content::RenderFrameHost* opener, |
| content::SiteInstance* source_site_instance, |
| int32_t route_id, |
| int32_t main_frame_route_id, |
| int32_t main_frame_widget_route_id, |
| content::mojom::WindowContainerType window_container_type, |
| const GURL& opener_url, |
| const std::string& frame_name, |
| const GURL& target_url, |
| const std::string& partition_id, |
| content::SessionStorageNamespace* session_storage_namespace) override { |
| // This method is invoked when attempting to open links in a new tab, e.g.: |
| // <a href="https://www.google.com/" target="_blank">Link</a> |
| client_->DidSuppressNavigation(target_url, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| /*from_user_gesture=*/true); |
| return false; |
| } |
| |
| 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); |
| } |
| |
| // 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<net::HttpResponseHeaders>( |
| navigation_handle->GetResponseHeaders()->raw_headers()) |
| : nullptr); |
| } |
| |
| void DidStopLoading() override { client_->DidStopLoading(); } |
| |
| std::unique_ptr<WebContents> 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<SkColor> background_color_; |
| |
| 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<content::Service*> 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<content::NavigableContentsDelegate> |
| ContentServiceDelegateImpl::CreateNavigableContentsDelegate( |
| const mojom::NavigableContentsParams& params, |
| mojom::NavigableContentsClient* client) { |
| return std::make_unique<NavigableContentsDelegateImpl>(browser_context_, |
| params, client); |
| } |
| |
| } // namespace content |