| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "android_webview/browser/renderer_host/aw_render_view_host_ext.h" |
| |
| #include "android_webview/browser/aw_browser_context.h" |
| #include "android_webview/browser/aw_contents.h" |
| #include "android_webview/browser/aw_contents_client_bridge.h" |
| #include "android_webview/common/aw_features.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/command_line.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| |
| namespace android_webview { |
| |
| // static |
| void AwRenderViewHostExt::BindFrameHost( |
| mojo::PendingAssociatedReceiver<mojom::FrameHost> receiver, |
| content::RenderFrameHost* rfh) { |
| auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); |
| if (!web_contents) |
| return; |
| auto* aw_contents = AwContents::FromWebContents(web_contents); |
| if (!aw_contents) |
| return; |
| auto* aw_rvh_ext = aw_contents->render_view_host_ext(); |
| if (!aw_rvh_ext) |
| return; |
| aw_rvh_ext->frame_host_receivers_.Bind(rfh, std::move(receiver)); |
| } |
| |
| AwRenderViewHostExt::AwRenderViewHostExt(AwRenderViewHostExtClient* client, |
| content::WebContents* contents) |
| : content::WebContentsObserver(contents), |
| client_(client), |
| frame_host_receivers_(contents, this) { |
| DCHECK(client_); |
| } |
| |
| AwRenderViewHostExt::~AwRenderViewHostExt() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| } |
| |
| void AwRenderViewHostExt::DocumentHasImages(DocumentHasImagesResult result) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (!web_contents()->GetRenderViewHost()) { |
| std::move(result).Run(false); |
| return; |
| } |
| |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) { |
| local_main_frame_remote->DocumentHasImage(std::move(result)); |
| } else { |
| // Still have to respond to the API call WebView#docuemntHasImages. |
| // Otherwise the listener of the response may be starved. |
| std::move(result).Run(false); |
| } |
| } |
| |
| void AwRenderViewHostExt::RequestNewHitTestDataAt( |
| const gfx::PointF& touch_center, |
| const gfx::SizeF& touch_area) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // If the new hit testing approach for touch start is enabled we just early |
| // return. |
| if (base::FeatureList::IsEnabled( |
| features::kWebViewHitTestInBlinkOnTouchStart)) { |
| return; |
| } |
| |
| // The following code is broken for OOPIF and fenced frames. The hit testing |
| // feature for touch start replaces this code and works correctly in those |
| // scenarios. For mitigating risk we've put the old code behind a feature |
| // flag. |
| // |
| // We only need to get blink::WebView on the renderer side to invoke the |
| // blink hit test Mojo method, so sending this message via LocalMainFrame |
| // interface is enough. |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) |
| local_main_frame_remote->HitTest(touch_center, touch_area); |
| } |
| |
| mojom::HitTestDataPtr AwRenderViewHostExt::TakeLastHitTestData() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return std::move(last_hit_test_data_); |
| } |
| |
| void AwRenderViewHostExt::SetTextZoomFactor(float factor) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) |
| local_main_frame_remote->SetTextZoomFactor(factor); |
| } |
| |
| void AwRenderViewHostExt::ResetScrollAndScaleState() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) |
| local_main_frame_remote->ResetScrollAndScaleState(); |
| } |
| |
| void AwRenderViewHostExt::SetInitialPageScale(double page_scale_factor) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) |
| local_main_frame_remote->SetInitialPageScale(page_scale_factor); |
| } |
| |
| void AwRenderViewHostExt::SetWillSuppressErrorPage(bool suppress) { |
| will_suppress_error_page_ = suppress; |
| } |
| |
| void AwRenderViewHostExt::SmoothScroll(int target_x, |
| int target_y, |
| base::TimeDelta duration) { |
| if (auto* local_main_frame_remote = GetLocalMainFrameRemote()) |
| local_main_frame_remote->SmoothScroll(target_x, target_y, duration); |
| } |
| |
| void AwRenderViewHostExt::DidStartNavigation( |
| content::NavigationHandle* navigation_handle) { |
| if (will_suppress_error_page_) |
| navigation_handle->SetSilentlyIgnoreErrors(); |
| } |
| |
| void AwRenderViewHostExt::DidFinishNavigation( |
| content::NavigationHandle* navigation_handle) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!navigation_handle->HasCommitted()) |
| return; |
| |
| // Only record a visit if the navigation affects user-facing session history |
| // (i.e. it occurs in the primary frame tree). |
| if (navigation_handle->IsInPrimaryMainFrame() || |
| (navigation_handle->GetParentFrame() && |
| navigation_handle->GetParentFrame()->GetPage().IsPrimary() && |
| navigation_handle->HasSubframeNavigationEntryCommitted())) { |
| AwBrowserContext::FromWebContents(web_contents()) |
| ->AddVisitedURLs(navigation_handle->GetRedirectChain()); |
| } |
| } |
| |
| void AwRenderViewHostExt::OnPageScaleFactorChanged(float page_scale_factor) { |
| client_->OnWebLayoutPageScaleFactorChanged(page_scale_factor); |
| } |
| |
| void AwRenderViewHostExt::UpdateHitTestData( |
| mojom::HitTestDataPtr hit_test_data) { |
| content::RenderFrameHost* render_frame_host = |
| frame_host_receivers_.GetCurrentTargetFrame(); |
| // Make sense from any frame of the active frame tree, because a focused |
| // node could be in either the mainframe or a subframe. |
| if (!render_frame_host->IsActive()) |
| return; |
| |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| last_hit_test_data_ = std::move(hit_test_data); |
| } |
| |
| void AwRenderViewHostExt::ContentsSizeChanged(const gfx::Size& contents_size) { |
| content::RenderFrameHost* render_frame_host = |
| frame_host_receivers_.GetCurrentTargetFrame(); |
| |
| // Only makes sense coming from the main frame of the current frame tree. |
| if (!render_frame_host->IsInPrimaryMainFrame()) |
| return; |
| |
| client_->OnWebLayoutContentsSizeChanged(contents_size); |
| } |
| |
| void AwRenderViewHostExt::ShouldOverrideUrlLoading( |
| const std::u16string& url, |
| bool has_user_gesture, |
| bool is_redirect, |
| bool is_main_frame, |
| ShouldOverrideUrlLoadingCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| bool ignore_navigation = false; |
| AwContentsClientBridge* client = |
| AwContentsClientBridge::FromWebContents(web_contents()); |
| if (client) { |
| if (!client->ShouldOverrideUrlLoading(url, has_user_gesture, is_redirect, |
| is_main_frame, &ignore_navigation)) { |
| // If the shouldOverrideUrlLoading call caused a java exception we should |
| // always return immediately here! |
| return; |
| } |
| } else { |
| LOG(WARNING) << "Failed to find the associated render view host for url: " |
| << url; |
| } |
| |
| std::move(callback).Run(ignore_navigation); |
| } |
| |
| mojom::LocalMainFrame* AwRenderViewHostExt::GetLocalMainFrameRemote() { |
| // Validate the local main frame matches what we have stored for the current |
| // main frame. Previously `local_main_frame_remote_` was adjusted in |
| // RenderFrameCreated/RenderFrameHostChanged events but the timings of when |
| // this class gets called vs others using this class might cause a TOU |
| // problem, so we validate it each time before use. |
| content::RenderFrameHost* main_frame = web_contents()->GetPrimaryMainFrame(); |
| content::GlobalRenderFrameHostId main_frame_id = main_frame->GetGlobalId(); |
| if (main_frame_global_id_ == main_frame_id) { |
| return local_main_frame_remote_.get(); |
| } |
| |
| local_main_frame_remote_.reset(); |
| |
| // Avoid accessing GetRemoteAssociatedInterfaces until the renderer is |
| // created. |
| if (!main_frame->IsRenderFrameLive()) { |
| main_frame_global_id_ = content::GlobalRenderFrameHostId(); |
| return nullptr; |
| } |
| |
| main_frame_global_id_ = main_frame_id; |
| main_frame->GetRemoteAssociatedInterfaces()->GetInterface( |
| local_main_frame_remote_.BindNewEndpointAndPassReceiver()); |
| return local_main_frame_remote_.get(); |
| } |
| |
| } // namespace android_webview |