blob: d2d00b3e7644f53cb251260ee4e5d34e6c930901 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2017 The Chromium Authors
liberatoa8da3b52017-05-02 20:23:512// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/android/dialog_overlay_impl.h"
6
Victor Hugo Vianna Silva94fdad22025-03-18 19:51:137#include <variant>
8
danakjc0270082020-09-15 22:28:009#include "content/browser/renderer_host/render_frame_host_impl.h"
Michael Thiessenc5c4fef2017-08-29 16:42:0410#include "content/browser/renderer_host/render_widget_host_view_base.h"
Jinsuk Kimd5d29a22017-08-29 01:39:4411#include "content/browser/web_contents/web_contents_impl.h"
Gabriel Charettec738e9582019-11-01 18:31:3312#include "content/public/browser/browser_task_traits.h"
Gabriel Charette790754c2018-03-16 21:32:5913#include "content/public/browser/browser_thread.h"
Richard Knollba182752021-06-01 10:49:5514#include "content/public/browser/content_browser_client.h"
Thomas Guilbert0267a42c2017-08-02 08:08:3315#include "content/public/browser/web_contents_delegate.h"
Richard Knollba182752021-06-01 10:49:5516#include "content/public/common/content_client.h"
liberatoa8da3b52017-05-02 20:23:5117#include "gpu/ipc/common/gpu_surface_tracker.h"
Frank Liberatod66bd9242020-11-20 21:05:4618#include "media/mojo/mojom/android_overlay.mojom.h"
Frank Liberato13bde9e2021-02-28 20:46:0019#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
Jinsuk Kimd5d29a22017-08-29 01:39:4420#include "ui/android/view_android_observer.h"
liberatoa8da3b52017-05-02 20:23:5121#include "ui/android/window_android.h"
22
Andrew Grieveecb885bb2024-05-29 18:14:1923// Must come after all headers that specialize FromJniType() / ToJniType().
24#include "content/public/android/content_jni_headers/DialogOverlayImpl_jni.h"
25
liberatoa8da3b52017-05-02 20:23:5126using base::android::AttachCurrentThread;
27using base::android::JavaParamRef;
28using base::android::ScopedJavaLocalRef;
29
30namespace content {
31
Daniel Bratell7aacf952017-11-21 17:51:2532static jlong JNI_DialogOverlayImpl_Init(JNIEnv* env,
33 const JavaParamRef<jobject>& obj,
34 jlong high,
35 jlong low,
36 jboolean power_efficient) {
liberatoa8da3b52017-05-02 20:23:5137 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Thomas Guilbert2d5ef442017-07-13 00:12:5538
Arthur Sonzognic686e8f2024-01-11 08:36:3739 std::optional<base::UnguessableToken> token =
Andrew Williams228be95c2023-01-26 15:13:0140 base::UnguessableToken::Deserialize(high, low);
Andrew Williamsc07db022023-01-24 13:38:5241 if (!token.has_value()) {
Andrew Williams068e6982022-12-29 01:46:5542 return 0;
43 }
44
Thomas Guilbert2d5ef442017-07-13 00:12:5545 RenderFrameHostImpl* rfhi =
Andrew Williamsc07db022023-01-24 13:38:5246 content::RenderFrameHostImpl::FromOverlayRoutingToken(token.value());
Thomas Guilbert2d5ef442017-07-13 00:12:5547
48 if (!rfhi)
49 return 0;
50
Daniel Cheng714f6212022-01-13 19:29:0951 // If the RenderFrameHost does not have a live RenderFrame, immediately bail
52 // out: not only is there nothing to do, the `RenderFrameDeleted()`
53 // notification to clean up the overlay would never be called.
54 if (!rfhi->IsRenderFrameLive())
55 return 0;
56
Thomas Guilbert2d5ef442017-07-13 00:12:5557 WebContentsImpl* web_contents_impl = static_cast<WebContentsImpl*>(
58 content::WebContents::FromRenderFrameHost(rfhi));
59
60 // If the overlay would not be immediately used, fail the request.
Sreeja Kamishettye49854f82021-06-02 00:52:0361 if (!rfhi->IsActive() || !web_contents_impl || web_contents_impl->IsHidden())
Thomas Guilbert2d5ef442017-07-13 00:12:5562 return 0;
63
[email protected]d57bc412017-08-09 20:30:1164 // Dialog-based overlays are not supported for persistent video.
John Abd-El-Malek74f0b95c2021-04-05 06:24:0265 if (web_contents_impl->has_persistent_video())
[email protected]d57bc412017-08-09 20:30:1166 return 0;
67
[email protected]e62e7ebf2017-09-21 17:14:4168 // If we require a power-efficient overlay, then approximate that with "is
69 // fullscreen". The reason is that we want to be somewhat sure that we don't
70 // have more layers than HWC can support, else SurfaceFlinger will fall back
71 // to GLES composition. In fullscreen mode, the android status bar is hidden,
72 // as is the nav bar (if present). The chrome activity surface also gets
73 // hidden when possible.
74 if (power_efficient && !web_contents_impl->IsFullscreen())
75 return 0;
76
Richard Knollba182752021-06-01 10:49:5577 bool observe_container_view =
78 GetContentClient()
79 ->browser()
80 ->ShouldObserveContainerViewLocationForDialogOverlays();
81
82 return reinterpret_cast<jlong>(new DialogOverlayImpl(
83 obj, rfhi, web_contents_impl, power_efficient, observe_container_view));
liberatoa8da3b52017-05-02 20:23:5184}
85
86DialogOverlayImpl::DialogOverlayImpl(const JavaParamRef<jobject>& obj,
Thomas Guilbert2d5ef442017-07-13 00:12:5587 RenderFrameHostImpl* rfhi,
[email protected]e62e7ebf2017-09-21 17:14:4188 WebContents* web_contents,
Richard Knollba182752021-06-01 10:49:5589 bool power_efficient,
90 bool observe_container_view)
[email protected]e62e7ebf2017-09-21 17:14:4191 : WebContentsObserver(web_contents),
92 rfhi_(rfhi),
Andrei Pascovicib4784022018-09-06 14:41:2293 power_efficient_(power_efficient),
Richard Knollba182752021-06-01 10:49:5594 observed_window_android_(false),
95 observe_container_view_(observe_container_view) {
liberatoa8da3b52017-05-02 20:23:5196 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Thomas Guilbert2d5ef442017-07-13 00:12:5597 DCHECK(rfhi_);
liberatoa8da3b52017-05-02 20:23:5198
99 JNIEnv* env = AttachCurrentThread();
100 obj_ = JavaObjectWeakGlobalRef(env, obj);
101
Carlos Caballerodaf65cf6d2021-03-29 10:30:00102 // Make sure RenderFrameDeleted will be called on RFH and thus we will clean
103 // up.
Nasko Oskov2511af422022-05-25 20:18:45104 CHECK(rfhi_->IsRenderFrameLive());
Jinsuk Kimd5d29a22017-08-29 01:39:44105 web_contents->GetNativeView()->AddObserver(this);
[email protected]251fe522017-07-11 00:54:06106
107 // Note that we're not allowed to call back into |obj| before it calls
108 // CompleteInit. However, the observer won't actually call us back until the
109 // token changes. As long as the java side calls us from the ui thread before
110 // returning, we won't send a callback before then.
111}
112
Andrew Grieveab2c9152025-07-02 19:53:13113void DialogOverlayImpl::CompleteInit(JNIEnv* env) {
[email protected]251fe522017-07-11 00:54:06114 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Thomas Guilbert0267a42c2017-08-02 08:08:33115
Thomas Guilbert50d8da42017-08-28 21:34:29116 WebContentsDelegate* delegate = web_contents()->GetDelegate();
117
118 if (!delegate) {
119 Stop();
120 return;
121 }
122
Andrew Grieveab2c9152025-07-02 19:53:13123 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
124
Thomas Guilbert0267a42c2017-08-02 08:08:33125 // Note: It's ok to call SetOverlayMode() directly here, because there can be
126 // at most one overlay alive at the time. This logic needs to be updated if
127 // ever AndroidOverlayProviderImpl.MAX_OVERLAYS > 1.
Thomas Guilbert50d8da42017-08-28 21:34:29128 delegate->SetOverlayMode(true);
Thomas Guilbert0267a42c2017-08-02 08:08:33129
Yuchen Liu2dce3f92022-11-22 21:48:20130 Java_DialogOverlayImpl_onWebContents(env, obj,
131 web_contents()->GetJavaWebContents());
132
[email protected]251fe522017-07-11 00:54:06133 // Send the initial token, if there is one. The observer will notify us about
134 // changes only.
Jinsuk Kimd5d29a22017-08-29 01:39:44135 if (auto* window = web_contents()->GetNativeView()->GetWindowAndroid()) {
Andrei Pascovicib4784022018-09-06 14:41:22136 RegisterWindowObserverIfNeeded(window);
Erin Yan7b5ad682022-07-19 21:17:55137 Java_DialogOverlayImpl_onWindowAndroid(env, obj, window->GetJavaObject());
liberatoa8da3b52017-05-02 20:23:51138 }
Richard Knollba182752021-06-01 10:49:55139
140 // Pass up a reference to the container view so we can observe its location.
141 // The observer will notify us if there is none yet.
142 StartObservingContainerView();
liberatoa8da3b52017-05-02 20:23:51143}
144
145DialogOverlayImpl::~DialogOverlayImpl() {
146 DCHECK_CURRENTLY_ON(BrowserThread::UI);
liberatoa8da3b52017-05-02 20:23:51147}
148
Thomas Guilbert2d5ef442017-07-13 00:12:55149void DialogOverlayImpl::Stop() {
Andrei Pascovicib4784022018-09-06 14:41:22150 UnregisterCallbacksIfNeeded();
Thomas Guilbert2d5ef442017-07-13 00:12:55151
152 JNIEnv* env = AttachCurrentThread();
153 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
154 if (!obj.is_null())
Torne (Richard Coles)6a1426cb2017-07-28 15:53:36155 Java_DialogOverlayImpl_onDismissed(env, obj);
Thomas Guilbert2d5ef442017-07-13 00:12:55156
157 obj_.reset();
158}
159
Andrew Grieveab2c9152025-07-02 19:53:13160void DialogOverlayImpl::Destroy(JNIEnv* env) {
liberatoa8da3b52017-05-02 20:23:51161 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Andrei Pascovicib4784022018-09-06 14:41:22162 UnregisterCallbacksIfNeeded();
liberatoa8da3b52017-05-02 20:23:51163 // We delete soon since this might be part of an onDismissed callback.
Gabriel Charettee7cdc5cd2020-05-27 23:35:05164 GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
liberatoa8da3b52017-05-02 20:23:51165}
166
[email protected]d6c17922017-06-26 18:48:06167void DialogOverlayImpl::GetCompositorOffset(
168 JNIEnv* env,
[email protected]d6c17922017-06-26 18:48:06169 const base::android::JavaParamRef<jobject>& rect) {
Jinsuk Kimd5d29a22017-08-29 01:39:44170 gfx::Point point =
[email protected]a11aa722017-08-30 21:07:43171 web_contents()->GetNativeView()->GetLocationOfContainerViewInWindow();
[email protected]d6c17922017-06-26 18:48:06172
Torne (Richard Coles)6a1426cb2017-07-28 15:53:36173 Java_DialogOverlayImpl_receiveCompositorOffset(env, rect, point.x(),
[email protected]251fe522017-07-11 00:54:06174 point.y());
[email protected]d6c17922017-06-26 18:48:06175}
176
Andrei Pascovicib4784022018-09-06 14:41:22177void DialogOverlayImpl::UnregisterCallbacksIfNeeded() {
liberatoa8da3b52017-05-02 20:23:51178 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Jinsuk Kimd5d29a22017-08-29 01:39:44179
180 if (!rfhi_)
liberatoa8da3b52017-05-02 20:23:51181 return;
182
Richard Knollba182752021-06-01 10:49:55183 // No need to track the container view location anymore.
184 StopObservingContainerView();
185
Thomas Guilbert0267a42c2017-08-02 08:08:33186 // We clear overlay mode here rather than in Destroy(), because we may have
187 // been called via a WebContentsDestroyed() event, and this might be the last
188 // opportunity we have to access web_contents().
Thomas Guilbert50d8da42017-08-28 21:34:29189 WebContentsDelegate* delegate = web_contents()->GetDelegate();
190 if (delegate)
191 delegate->SetOverlayMode(false);
Andrei Pascovicib4784022018-09-06 14:41:22192 if (observed_window_android_) {
193 auto* window_android = web_contents()->GetNativeView()->GetWindowAndroid();
194 if (window_android)
195 window_android->RemoveObserver(this);
196 observed_window_android_ = false;
197 }
Jinsuk Kimd5d29a22017-08-29 01:39:44198 web_contents()->GetNativeView()->RemoveObserver(this);
Thomas Guilbert2d5ef442017-07-13 00:12:55199 rfhi_ = nullptr;
liberatoa8da3b52017-05-02 20:23:51200}
201
Thomas Guilbert2d5ef442017-07-13 00:12:55202void DialogOverlayImpl::RenderFrameDeleted(RenderFrameHost* render_frame_host) {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI);
204 if (render_frame_host == rfhi_)
205 Stop();
206}
207
208void DialogOverlayImpl::RenderFrameHostChanged(RenderFrameHost* old_host,
209 RenderFrameHost* new_host) {
210 DCHECK_CURRENTLY_ON(BrowserThread::UI);
211 if (old_host == rfhi_)
212 Stop();
213}
214
Francois Dorayfe4a1772018-02-17 04:17:09215void DialogOverlayImpl::OnVisibilityChanged(content::Visibility visibility) {
Thomas Guilbert2d5ef442017-07-13 00:12:55216 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Francois Dorayfe4a1772018-02-17 04:17:09217 if (visibility == content::Visibility::HIDDEN)
218 Stop();
Thomas Guilbert2d5ef442017-07-13 00:12:55219}
220
Andrei Pascovicib4784022018-09-06 14:41:22221void DialogOverlayImpl::OnRootWindowVisibilityChanged(bool visible) {
222 if (!visible)
223 Stop();
224}
225
Thomas Guilbert2d5ef442017-07-13 00:12:55226void DialogOverlayImpl::WebContentsDestroyed() {
227 DCHECK_CURRENTLY_ON(BrowserThread::UI);
228 Stop();
liberatoa8da3b52017-05-02 20:23:51229}
230
Muyao Xudb226be52023-12-01 04:23:09231void DialogOverlayImpl::DidToggleFullscreenModeForTab(bool entered_fullscreen,
232 bool will_cause_resize) {
[email protected]e62e7ebf2017-09-21 17:14:41233 DCHECK_CURRENTLY_ON(BrowserThread::UI);
234
235 // If the caller doesn't care about power-efficient overlays, then don't send
236 // any callbacks about state change.
237 if (!power_efficient_)
238 return;
239
240 JNIEnv* env = AttachCurrentThread();
241 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
242 if (!obj.is_null())
243 Java_DialogOverlayImpl_onPowerEfficientState(env, obj, entered_fullscreen);
244}
245
liberatoa8da3b52017-05-02 20:23:51246void DialogOverlayImpl::OnAttachedToWindow() {
247 DCHECK_CURRENTLY_ON(BrowserThread::UI);
248 JNIEnv* env = AttachCurrentThread();
249
Erin Yan7b5ad682022-07-19 21:17:55250 auto* window = web_contents()->GetNativeView()->GetWindowAndroid();
251 if (window)
Andrei Pascovicib4784022018-09-06 14:41:22252 RegisterWindowObserverIfNeeded(window);
Erin Yan7b5ad682022-07-19 21:17:55253
liberatoa8da3b52017-05-02 20:23:51254 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
255 if (!obj.is_null())
Erin Yan7b5ad682022-07-19 21:17:55256 Java_DialogOverlayImpl_onWindowAndroid(env, obj, window->GetJavaObject());
Richard Knollba182752021-06-01 10:49:55257
258 StartObservingContainerView();
liberatoa8da3b52017-05-02 20:23:51259}
260
261void DialogOverlayImpl::OnDetachedFromWindow() {
262 JNIEnv* env = AttachCurrentThread();
263 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
264 if (!obj.is_null())
Erin Yan7b5ad682022-07-19 21:17:55265 Java_DialogOverlayImpl_onWindowAndroid(env, obj, nullptr);
Andrei Pascovici17d5845e2018-09-20 22:09:07266 Stop();
liberatoa8da3b52017-05-02 20:23:51267}
268
Andrei Pascovicib4784022018-09-06 14:41:22269void DialogOverlayImpl::RegisterWindowObserverIfNeeded(
270 ui::WindowAndroid* window) {
271 if (!observed_window_android_) {
272 observed_window_android_ = true;
273 window->AddObserver(this);
274 }
275}
276
Richard Knollba182752021-06-01 10:49:55277void DialogOverlayImpl::StartObservingContainerView() {
278 ObserveContainerViewIfNeeded(
279 web_contents()->GetNativeView()->GetContainerView());
280}
281
282void DialogOverlayImpl::StopObservingContainerView() {
283 ObserveContainerViewIfNeeded(/*container_view=*/nullptr);
284}
285
286void DialogOverlayImpl::ObserveContainerViewIfNeeded(
287 const ScopedJavaLocalRef<jobject>& container_view) {
288 if (!observe_container_view_)
289 return;
290
291 JNIEnv* env = AttachCurrentThread();
292 ScopedJavaLocalRef<jobject> obj = obj_.get(env);
293 if (!obj.is_null())
294 Java_DialogOverlayImpl_observeContainerView(env, obj, container_view);
295}
296
Frank Liberato13bde9e2021-02-28 20:46:00297// Helper class that has permission to talk to SyncCallRestrictions. Rather
298// than friend the function directly, which has an odd signature, friend a class
299// that knows how to do the work.
300class AndroidOverlaySyncHelper {
301 public:
302 static void MakeSyncCall(media::mojom::AndroidOverlayClient* remote) {
303 mojo::SyncCallRestrictions::ScopedAllowSyncCall scoped_allow;
304 remote->OnSynchronouslyDestroyed();
305 }
306};
307
Frank Liberatod66bd9242020-11-20 21:05:46308static void JNI_DialogOverlayImpl_NotifyDestroyedSynchronously(
309 JNIEnv* env,
Ken Rockot8e8c8c82022-03-04 18:21:38310 jlong message_pipe_handle) {
Frank Liberatod66bd9242020-11-20 21:05:46311 mojo::MessagePipeHandle handle(message_pipe_handle);
Frank Liberato46246e02020-12-08 22:46:48312 mojo::ScopedMessagePipeHandle scoped_handle(handle);
Frank Liberatod66bd9242020-11-20 21:05:46313 mojo::Remote<media::mojom::AndroidOverlayClient> remote(
314 mojo::PendingRemote<media::mojom::AndroidOverlayClient>(
315 std::move(scoped_handle),
316 media::mojom::AndroidOverlayClient::Version_));
Frank Liberato13bde9e2021-02-28 20:46:00317 // This prevents crashes, though it's unclear how we'd have a null remote.
318 // https://crbug.com/1155313 .
Frank Liberato72a96aa2020-12-15 18:49:45319 if (!remote.is_bound())
320 return;
Frank Liberato13bde9e2021-02-28 20:46:00321 AndroidOverlaySyncHelper::MakeSyncCall(remote.get());
Frank Liberatod66bd9242020-11-20 21:05:46322 // Note that we don't take back the mojo message pipe. We let it close when
323 // `remote` goes out of scope.
324}
325
Daniel Bratell7aacf952017-11-21 17:51:25326static jint JNI_DialogOverlayImpl_RegisterSurface(
327 JNIEnv* env,
Daniel Bratell7aacf952017-11-21 17:51:25328 const JavaParamRef<jobject>& surface) {
liberatoa8da3b52017-05-02 20:23:51329 DCHECK_CURRENTLY_ON(BrowserThread::UI);
330 return gpu::GpuSurfaceTracker::Get()->AddSurfaceForNativeWidget(
Kartar Singh169f17572024-09-18 13:01:59331 gpu::SurfaceRecord(gl::ScopedJavaSurface(surface, /*auto_release=*/false),
332 /*can_be_used_with_surface_control=*/false));
liberatoa8da3b52017-05-02 20:23:51333}
334
Daniel Bratell7aacf952017-11-21 17:51:25335static void JNI_DialogOverlayImpl_UnregisterSurface(
liberatoa8da3b52017-05-02 20:23:51336 JNIEnv* env,
liberatoa8da3b52017-05-02 20:23:51337 jint surface_id) {
338 DCHECK_CURRENTLY_ON(BrowserThread::UI);
339 gpu::GpuSurfaceTracker::Get()->RemoveSurface(surface_id);
340}
341
Daniel Bratell7aacf952017-11-21 17:51:25342static ScopedJavaLocalRef<jobject>
343JNI_DialogOverlayImpl_LookupSurfaceForTesting(
[email protected]251fe522017-07-11 00:54:06344 JNIEnv* env,
[email protected]251fe522017-07-11 00:54:06345 jint surfaceId) {
Kartar Singh169f17572024-09-18 13:01:59346 auto surface_record =
347 gpu::GpuSurfaceTracker::Get()->AcquireJavaSurface(surfaceId);
Victor Hugo Vianna Silva94fdad22025-03-18 19:51:13348 if (!std::holds_alternative<gl::ScopedJavaSurface>(
Kartar Singh169f17572024-09-18 13:01:59349 surface_record.surface_variant)) {
Bo Liu30286a82022-12-16 22:13:38350 return nullptr;
351 }
352 return ScopedJavaLocalRef<jobject>(
Victor Hugo Vianna Silva94fdad22025-03-18 19:51:13353 std::get<gl::ScopedJavaSurface>(surface_record.surface_variant)
Kartar Singh169f17572024-09-18 13:01:59354 .j_surface());
[email protected]251fe522017-07-11 00:54:06355}
356
liberatoa8da3b52017-05-02 20:23:51357} // namespace content