diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.cc b/components/permissions/android/permission_prompt/permission_prompt_android.cc index 9d822ae5b0ddd7ea8b80987b19b6ce23b9c227ae..40f263ea1e33fe4dd6c807b7e5b606dcd96e4e56 100644 --- a/components/permissions/android/permission_prompt/permission_prompt_android.cc +++ b/components/permissions/android/permission_prompt/permission_prompt_android.cc @@ -33,6 +33,11 @@ PermissionPromptAndroid::GetTabSwitchingBehavior() { return TabSwitchingBehavior::kKeepPromptAlive; } +absl::optional PermissionPromptAndroid::GetViewBoundsInScreen() + const { + return absl::nullopt; +} + void PermissionPromptAndroid::Closing() { delegate_->Dismiss(); } diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.h b/components/permissions/android/permission_prompt/permission_prompt_android.h index 532ede4ffbc641071a4e4d48c1ad06bec377accc..39e962f4e11d222e12307babc30ccffa47b98ba8 100644 --- a/components/permissions/android/permission_prompt/permission_prompt_android.h +++ b/components/permissions/android/permission_prompt/permission_prompt_android.h @@ -13,6 +13,8 @@ #include "components/permissions/permission_prompt.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permissions_client.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/geometry/rect.h" namespace content { class WebContents; @@ -40,6 +42,7 @@ class PermissionPromptAndroid : public PermissionPrompt { // PermissionPrompt: bool UpdateAnchor() override; TabSwitchingBehavior GetTabSwitchingBehavior() override; + absl::optional GetViewBoundsInScreen() const override; void Closing(); void Accept(); diff --git a/components/permissions/permission_manager.cc b/components/permissions/permission_manager.cc index 128fb3a8b1a99d5be25f67c77de29899076720a8..d4e57ea8063d118bbc1761b121046af66b17423e 100644 --- a/components/permissions/permission_manager.cc +++ b/components/permissions/permission_manager.cc @@ -19,6 +19,7 @@ #include "components/permissions/features.h" #include "components/permissions/permission_context_base.h" #include "components/permissions/permission_request_id.h" +#include "components/permissions/permission_request_manager.h" #include "components/permissions/permission_result.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" @@ -519,6 +520,13 @@ void PermissionManager::UnsubscribePermissionStatusChange( } } +absl::optional PermissionManager::GetExclusionAreaBoundsInScreen( + content::WebContents* web_contents) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto* manager = PermissionRequestManager::FromWebContents(web_contents); + return manager ? manager->GetPromptBubbleViewBoundsInScreen() : absl::nullopt; +} + void PermissionManager::OnPermissionsRequestResponseStatus( PendingRequestLocalId request_local_id, int permission_id, diff --git a/components/permissions/permission_manager.h b/components/permissions/permission_manager.h index c7a0b1188bb309f9eba61a8dc60dcf37f8e5198c..2d7bf66b75bcbd2aa25394009aa48fd1030a91db 100644 --- a/components/permissions/permission_manager.h +++ b/components/permissions/permission_manager.h @@ -31,6 +31,7 @@ namespace content { class BrowserContext; class RenderFrameHost; class RenderProcessHost; +class WebContents; } class GeolocationPermissionContextDelegateTests; @@ -161,6 +162,8 @@ class PermissionManager : public KeyedService, override; void UnsubscribePermissionStatusChange( SubscriptionId subscription_id) override; + absl::optional GetExclusionAreaBoundsInScreen( + content::WebContents* web_contents) const override; // Called when a permission was decided for a given PendingRequest. The // PendingRequest is identified by its |request_local_id| and the permission diff --git a/components/permissions/permission_prompt.h b/components/permissions/permission_prompt.h index f05f9550a8ebb23f1faf931458d7db778d8dd00d..e93244b0edcbfd93f260f070b57c30dacaead434 100644 --- a/components/permissions/permission_prompt.h +++ b/components/permissions/permission_prompt.h @@ -10,6 +10,8 @@ #include "base/functional/callback.h" #include "components/permissions/permission_ui_selector.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/geometry/rect.h" #include "url/gurl.h" namespace content { @@ -142,6 +144,9 @@ class PermissionPrompt { // Get the type of prompt UI shown for metrics. virtual PermissionPromptDisposition GetPromptDisposition() const = 0; + + // Get the prompt view bounds in screen coordinates. + virtual absl::optional GetViewBoundsInScreen() const = 0; }; } // namespace permissions diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc index 02ebba3f7bdfcca7aece67e65a8abde3ae275df5..374daebc038eeb0c4cb8b8e8e0b74fa0f917dd36 100644 --- a/components/permissions/permission_request_manager.cc +++ b/components/permissions/permission_request_manager.cc @@ -726,6 +726,11 @@ bool PermissionRequestManager::RecreateView() { return true; } +absl::optional +PermissionRequestManager::GetPromptBubbleViewBoundsInScreen() const { + return view_ ? view_->GetViewBoundsInScreen() : absl::nullopt; +} + PermissionRequestManager::PermissionRequestManager( content::WebContents* web_contents) : content::WebContentsObserver(web_contents), diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h index a0da5dafebb729a41bd89dbba4f47db3bb475003..1302b745fb3b7092c67d4a72f95781709fa6c5b6 100644 --- a/components/permissions/permission_request_manager.h +++ b/components/permissions/permission_request_manager.h @@ -23,6 +23,8 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/geometry/rect.h" class GURL; @@ -174,6 +176,10 @@ class PermissionRequestManager content::WebContents* GetAssociatedWebContents() override; bool RecreateView() override; + // Returns the bounds of the active permission prompt view if we're + // displaying one. + absl::optional GetPromptBubbleViewBoundsInScreen() const; + void set_manage_clicked() { did_click_manage_ = true; } void set_learn_more_clicked() { did_click_learn_more_ = true; } diff --git a/components/permissions/test/mock_permission_prompt.cc b/components/permissions/test/mock_permission_prompt.cc index 578fea33f5bb8a1d8dfebfff7f45e689ddc6181e..0e186f03630e3b67f71ade32be9bb7b7e70e29c5 100644 --- a/components/permissions/test/mock_permission_prompt.cc +++ b/components/permissions/test/mock_permission_prompt.cc @@ -44,6 +44,10 @@ PermissionPromptDisposition MockPermissionPrompt::GetPromptDisposition() const { #endif } +absl::optional MockPermissionPrompt::GetViewBoundsInScreen() const { + return absl::make_optional(100, 100, 100, 100); +} + MockPermissionPrompt::MockPermissionPrompt(MockPermissionPromptFactory* factory, Delegate* delegate) : factory_(factory), delegate_(delegate) { diff --git a/components/permissions/test/mock_permission_prompt.h b/components/permissions/test/mock_permission_prompt.h index 579e093d95279fdf0523721f554002e48d4beb38..551afdf9187e72a524a83ce4ff26b9d744a861de 100644 --- a/components/permissions/test/mock_permission_prompt.h +++ b/components/permissions/test/mock_permission_prompt.h @@ -23,6 +23,7 @@ class MockPermissionPrompt : public PermissionPrompt { bool UpdateAnchor() override; TabSwitchingBehavior GetTabSwitchingBehavior() override; PermissionPromptDisposition GetPromptDisposition() const override; + absl::optional GetViewBoundsInScreen() const override; bool IsVisible(); diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc index 6ef8bc88170c48b5275f8ae76398d9e12b305387..0fbce298f81c10e3353d3b8961469f4f5b20729d 100644 --- a/content/browser/permissions/permission_controller_impl.cc +++ b/content/browser/permissions/permission_controller_impl.cc @@ -748,6 +748,18 @@ bool PermissionControllerImpl::IsSubscribedToPermissionChangeEvent( permission_service_context->GetOnchangeEventListeners().end(); } +absl::optional +PermissionControllerImpl::GetExclusionAreaBoundsInScreen( + WebContents* web_contents) const { + if (exclusion_area_bounds_for_tests_.has_value()) { + return exclusion_area_bounds_for_tests_; + } + PermissionControllerDelegate* delegate = + browser_context_->GetPermissionControllerDelegate(); + return delegate ? delegate->GetExclusionAreaBoundsInScreen(web_contents) + : absl::nullopt; +} + void PermissionControllerImpl::NotifyEventListener() { if (onchange_listeners_callback_for_tests_.has_value()) { onchange_listeners_callback_for_tests_.value().Run(); diff --git a/content/browser/permissions/permission_controller_impl.h b/content/browser/permissions/permission_controller_impl.h index 409e2b7984764582dc0c17faf0111985df2e3f01..53665b91bbf0792818dbec7b4f8c34c6742aac35 100644 --- a/content/browser/permissions/permission_controller_impl.h +++ b/content/browser/permissions/permission_controller_impl.h @@ -13,6 +13,8 @@ #include "content/common/content_export.h" #include "content/public/browser/permission_controller.h" #include "content/public/browser/permission_overrides.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/geometry/rect.h" #include "url/gurl.h" #include "url/origin.h" @@ -26,6 +28,7 @@ class BrowserContext; class PermissionControllerImplTest; class RenderProcessHost; class PermissionServiceImpl; +class WebContents; struct PermissionResult; using blink::PermissionType; @@ -94,10 +97,21 @@ class CONTENT_EXPORT PermissionControllerImpl : public PermissionController { void UnsubscribePermissionStatusChange( SubscriptionId subscription_id) override; + // If there's currently a permission prompt bubble for the given WebContents, + // returns the bounds of the bubble view as exclusion area in screen + // coordinates. + absl::optional GetExclusionAreaBoundsInScreen( + WebContents* web_contents) const; + void add_notify_listener_observer_for_tests(base::RepeatingClosure callback) { onchange_listeners_callback_for_tests_ = std::move(callback); } + void set_exclusion_area_bounds_for_tests( + const absl::optional& bounds) { + exclusion_area_bounds_for_tests_ = bounds; + } + private: friend class PermissionControllerImplTest; friend class PermissionServiceImpl; @@ -181,6 +195,8 @@ class CONTENT_EXPORT PermissionControllerImpl : public PermissionController { absl::optional onchange_listeners_callback_for_tests_; + absl::optional exclusion_area_bounds_for_tests_; + // Note that SubscriptionId is distinct from // PermissionControllerDelegate::SubscriptionId, and the concrete ID values // may be different as well. diff --git a/content/browser/renderer_host/DEPS b/content/browser/renderer_host/DEPS index 01d4e74c12039d487db6c5a1660ccc46d2d5df52..160b345993ab9de0f5969e79050e2ea6902cb58f 100644 --- a/content/browser/renderer_host/DEPS +++ b/content/browser/renderer_host/DEPS @@ -46,5 +46,6 @@ specific_include_rules = { ], "popup_menu_helper_mac.mm": [ "+content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h", + "+content/public/browser/web_contents.h", ] } diff --git a/content/browser/renderer_host/popup_menu_helper_mac.mm b/content/browser/renderer_host/popup_menu_helper_mac.mm index 219d0c2a61d55ca53f73c59b6dfec9c1358a7448..08e2fa43a629d8dae1f737cb984a00530226fbc7 100644 --- a/content/browser/renderer_host/popup_menu_helper_mac.mm +++ b/content/browser/renderer_host/popup_menu_helper_mac.mm @@ -9,12 +9,14 @@ #import "base/message_loop/message_pump_mac.h" #include "base/task/current_thread.h" #import "content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h" +#include "content/browser/permissions/permission_controller_impl.h" #include "content/browser/renderer_host/frame_tree.h" #include "content/browser/renderer_host/frame_tree_node.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/renderer_host/render_widget_host_view_mac.h" #include "content/browser/renderer_host/webmenurunner_mac.h" +#include "content/public/browser/web_contents.h" #import "ui/base/cocoa/base_view.h" namespace content { @@ -59,6 +61,25 @@ void PopupMenuHelper::ShowPopupMenu( if (!g_allow_showing_popup_menus) return; + RenderWidgetHostViewMac* rwhvm = GetRenderWidgetHostView(); + auto* web_contents = rwhvm->GetWebContents(); + // Convert element_bounds to be in screen. + gfx::Rect client_area = web_contents->GetContainerBounds(); + gfx::Rect bounds_in_screen = bounds + client_area.OffsetFromOrigin(); + // The new popup menu would overlap the permission prompt, which could lead to + // users making decisions based on incorrect information. We should close the + // popup if it intersects with the permission prompt. + auto permission_exclusion_area_bounds = + PermissionControllerImpl::FromBrowserContext( + web_contents->GetBrowserContext()) + ->GetExclusionAreaBoundsInScreen(web_contents); + if (permission_exclusion_area_bounds && + permission_exclusion_area_bounds->Intersects(bounds_in_screen)) { + popup_client_->DidCancel(); + delegate_->OnMenuClosed(); // May delete |this|. + return; + } + // Retain the Cocoa view for the duration of the pop-up so that it can't be // dealloced if my Destroy() method is called while the pop-up's up (which // would in turn delete me, causing a crash once the -runMenuInView diff --git a/content/browser/renderer_host/render_widget_host_browsertest.cc b/content/browser/renderer_host/render_widget_host_browsertest.cc index 158187f47e24eb1af23b3b65f261d7b0d1fc32bc..f3a7eb5dfbd5d91f4a6e7488caadbba85781afe1 100644 --- a/content/browser/renderer_host/render_widget_host_browsertest.cc +++ b/content/browser/renderer_host/render_widget_host_browsertest.cc @@ -15,6 +15,8 @@ #include "base/test/test_timeouts.h" #include "base/time/time.h" #include "build/build_config.h" +#include "content/browser/permissions/permission_controller_impl.h" +#include "content/browser/permissions/permission_controller_impl.h" #include "content/browser/renderer_host/input/input_router_impl.h" #include "content/browser/renderer_host/input/synthetic_smooth_drag_gesture.h" #include "content/browser/renderer_host/input/touch_action_filter.h" @@ -47,6 +49,10 @@ #include "ui/gfx/geometry/size.h" #include "ui/latency/latency_info.h" +#if BUILDFLAG(IS_MAC) +#include "third_party/blink/public/mojom/choosers/popup_menu.mojom-blink.h" +#endif + namespace content { namespace { @@ -649,6 +655,179 @@ IN_PROC_BROWSER_TEST_F(RenderWidgetHostSitePerProcessTest, } #endif +#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) + +namespace { + +// Intercept PopupWidgetHost::ShowPopup to override the initial bounds +class ShowPopupInterceptor + : public blink::mojom::PopupWidgetHostInterceptorForTesting { + public: + ShowPopupInterceptor(WebContentsImpl* web_contents, + RenderFrameHostImpl* frame_host, + const gfx::Rect& overriden_bounds) + : overriden_bounds_(overriden_bounds), frame_host_(frame_host) { + frame_host_->SetCreateNewPopupCallbackForTesting(base::BindRepeating( + &ShowPopupInterceptor::DidCreatePopupWidget, base::Unretained(this))); + } + + ShowPopupInterceptor(const ShowPopupInterceptor&) = delete; + ShowPopupInterceptor& operator=(const ShowPopupInterceptor&) = delete; + ~ShowPopupInterceptor() override { + if (auto* rwhi = RenderWidgetHostImpl::FromID(process_id_, routing_id_)) { + std::ignore = + rwhi->popup_widget_host_receiver_for_testing().SwapImplForTesting( + rwhi); + } + + frame_host_->SetCreateNewPopupCallbackForTesting(base::NullCallback()); + } + + void Wait() { run_loop_.Run(); } + + // blink::mojom::PopupWidgetHostInterceptorForTesting: + blink::mojom::PopupWidgetHost* GetForwardingInterface() override { + DCHECK_NE(MSG_ROUTING_NONE, routing_id_); + return RenderWidgetHostImpl::FromID(process_id_, routing_id_); + } + + void ShowPopup(const gfx::Rect& initial_rect, + const gfx::Rect& initial_anchor_rect, + ShowPopupCallback callback) override { + GetForwardingInterface()->ShowPopup(overriden_bounds_, initial_anchor_rect, + std::move(callback)); + run_loop_.Quit(); + } + + void DidCreatePopupWidget(RenderWidgetHostImpl* render_widget_host) { + process_id_ = render_widget_host->GetProcess()->GetID(); + routing_id_ = render_widget_host->GetRoutingID(); + std::ignore = render_widget_host->popup_widget_host_receiver_for_testing() + .SwapImplForTesting(this); + } + + int last_routing_id() const { return routing_id_; } + + private: + base::RunLoop run_loop_; + gfx::Rect overriden_bounds_; + int32_t routing_id_ = MSG_ROUTING_NONE; + int32_t process_id_ = 0; + raw_ptr frame_host_; +}; + +#if BUILDFLAG(IS_MAC) + +// Intercepts calls to LocalFrameHost::ShowPopupMenu method(), to override +// initial bounds and hook the `PopupMenuClient` +class ShowPopupMenuInterceptor + : public blink::mojom::LocalFrameHostInterceptorForTesting, + public blink::mojom::PopupMenuClient { + public: + explicit ShowPopupMenuInterceptor(RenderFrameHostImpl* render_frame_host, + const gfx::Rect& overriden_bounds) + : overriden_bounds_(overriden_bounds), + render_frame_host_(render_frame_host), + swapped_impl_( + render_frame_host_->local_frame_host_receiver_for_testing(), + this) {} + + ~ShowPopupMenuInterceptor() override = default; + + LocalFrameHost* GetForwardingInterface() override { + return render_frame_host_; + } + + void Wait() { run_loop_.Run(); } + + void ShowPopupMenu( + mojo::PendingRemote popup_client, + const gfx::Rect& bounds, + int32_t item_height, + double font_size, + int32_t selected_item, + std::vector menu_items, + bool right_aligned, + bool allow_multiple_selection) override { + GetForwardingInterface()->ShowPopupMenu( + receiver_.BindNewPipeAndPassRemote(), overriden_bounds_, item_height, + font_size, selected_item, std::move(menu_items), right_aligned, + allow_multiple_selection); + } + + void DidAcceptIndices(const std::vector& indices) override { + receiver_.reset(); + } + + void DidCancel() override { + is_cancelled_ = true; + receiver_.reset(); + run_loop_.Quit(); + } + + bool is_cancelled() const { return is_cancelled_; } + + private: + base::RunLoop run_loop_; + bool is_cancelled_{false}; + gfx::Rect overriden_bounds_; + raw_ptr render_frame_host_; + mojo::test::ScopedSwapImplForTesting< + mojo::AssociatedReceiver> + swapped_impl_; + mojo::Receiver receiver_{this}; +}; +#endif // BUILDFLAG(IS_MAC) + +} // namespace + +IN_PROC_BROWSER_TEST_F(RenderWidgetHostSitePerProcessTest, + BrowserClosesPopupIntersectsPermissionPrompt) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/site_isolation/page-with-select.html")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + auto* contents = static_cast(web_contents()); + FrameTreeNode* root = contents->GetPrimaryFrameTree().root(); + RenderFrameHostImpl* root_frame_host = root->current_frame_host(); + + // TODO(crbug.com/1181150): Crash when we attempt to use a mock prompt here. + // After the ticket is fixed, remove the shortcut of getting bounds and use + // the `MockPermissionPromptFactory` instead. + // Create a popup widget and wait for the RenderWidgetHost to be shown. + gfx::Rect permission_exclusion_area_bounds(100, 100, 100, 100); + static_cast( + root_frame_host->GetBrowserContext()->GetPermissionController()) + ->set_exclusion_area_bounds_for_tests(permission_exclusion_area_bounds); +#if BUILDFLAG(IS_MAC) + ShowPopupMenuInterceptor show_popup_menu_interceptor( + root_frame_host, permission_exclusion_area_bounds - + contents->GetContainerBounds().OffsetFromOrigin()); +#else + ShowPopupInterceptor show_popup_interceptor(contents, root_frame_host, + permission_exclusion_area_bounds); +#endif // BUILDFLAG(IS_MAC) + + NativeWebKeyboardEvent event( + blink::WebKeyboardEvent::Type::kChar, blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + event.text[0] = ' '; + EXPECT_TRUE(ExecJs(root_frame_host, "focusSelectMenu();")); + root_frame_host->GetRenderWidgetHost()->ForwardKeyboardEvent(event); + +#if BUILDFLAG(IS_MAC) + show_popup_menu_interceptor.Wait(); + ASSERT_TRUE(show_popup_menu_interceptor.is_cancelled()); +#else + show_popup_interceptor.Wait(); + ASSERT_FALSE( + RenderWidgetHost::FromID(root_frame_host->GetProcess()->GetID(), + show_popup_interceptor.last_routing_id())); +#endif // BUILDFLAG(IS_MAC) +} + +#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) + class RenderWidgetHostFullscreenScreenSizeBrowserTest : public RenderWidgetHostBrowserTest, public testing::WithParamInterface { diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 53349c63f70c0ef08dc2a120c45ba83495778950..f6367462fdb19225525ae19cd9b60fd76f765694 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -4617,10 +4617,19 @@ void WebContentsImpl::ShowCreatedWidget(int process_id, bottom_right.y() - origin.y()); } + RenderWidgetHostImpl* render_widget_host_impl = widget_host_view->host(); + auto permission_exclusion_area_bounds = + PermissionControllerImpl::FromBrowserContext(GetBrowserContext()) + ->GetExclusionAreaBoundsInScreen(outermost_web_contents); + if (permission_exclusion_area_bounds && + permission_exclusion_area_bounds->Intersects(transformed_rect)) { + render_widget_host_impl->ShutdownAndDestroyWidget(true); + return; + } + widget_host_view->InitAsPopup(view, transformed_rect, transformed_anchor_rect); - RenderWidgetHostImpl* render_widget_host_impl = widget_host_view->host(); // Renderer-owned popup widgets wait for the renderer to request for them // to be shown. We signal that this condition is satisfied by calling Init(). render_widget_host_impl->Init(); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 5bba5ee99ad6f627361819a76892e3ecf23bb824..2465c35cbe5e987f18e5142808823efe7309e98e 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -1562,6 +1562,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, PopupWindowBrowserNavResumeLoad); FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest, SuppressedPopupWindowBrowserNavResumeLoad); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostSitePerProcessTest, + BrowserClosesPopupIntersectsPermissionPrompt); // So |find_request_manager_| can be accessed for testing. friend class FindRequestManagerTest; diff --git a/content/public/browser/permission_controller_delegate.cc b/content/public/browser/permission_controller_delegate.cc index f9fecad73c62615f0d24671b1084a2f608126b00..d0d483e89ad86e999036d37dd4ce32e1ab5b10f9 100644 --- a/content/public/browser/permission_controller_delegate.cc +++ b/content/public/browser/permission_controller_delegate.cc @@ -5,6 +5,7 @@ #include "content/public/browser/permission_controller_delegate.h" #include "content/public/browser/permission_result.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" namespace content { @@ -22,4 +23,10 @@ PermissionControllerDelegate::GetPermissionResultForCurrentDocument( PermissionStatusSource::UNSPECIFIED); } +absl::optional +PermissionControllerDelegate::GetExclusionAreaBoundsInScreen( + content::WebContents* web_contents) const { + return absl::nullopt; +} + } // namespace content diff --git a/content/public/browser/permission_controller_delegate.h b/content/public/browser/permission_controller_delegate.h index ef9dfe15af96c39503eeedc026075a00a9738df8..8041ba2a8b8adbcfaf44ca74d77abd26df6f4686 100644 --- a/content/public/browser/permission_controller_delegate.h +++ b/content/public/browser/permission_controller_delegate.h @@ -8,7 +8,9 @@ #include "base/types/id_type.h" #include "content/common/content_export.h" #include "content/public/browser/permission_result.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h" +#include "ui/gfx/geometry/rect.h" class GURL; @@ -23,6 +25,7 @@ enum class PermissionType; namespace content { class RenderFrameHost; class RenderProcessHost; +class WebContents; class CONTENT_EXPORT PermissionControllerDelegate { public: @@ -144,6 +147,14 @@ class CONTENT_EXPORT PermissionControllerDelegate { virtual void UnsubscribePermissionStatusChange( SubscriptionId subscription_id) = 0; + // If there's currently a permission UI presenting for the given WebContents, + // returns bounds of the view as an exclusion area. We will use these bounds + // to avoid situations where users may make bad decisions based on incorrect + // contextual information (due to content or widgets overlaying the exclusion + // area) + virtual absl::optional GetExclusionAreaBoundsInScreen( + WebContents* web_contents) const; + // Returns whether permission can be overridden. virtual bool IsPermissionOverridable( blink::PermissionType permission, diff --git a/content/public/test/mock_permission_controller.cc b/content/public/test/mock_permission_controller.cc index a6b087f5ce4e209c9398a2f42952ea2950adce6e..e62efee1e1fbdbfae679d54590a0ee0e7e87bb52 100644 --- a/content/public/test/mock_permission_controller.cc +++ b/content/public/test/mock_permission_controller.cc @@ -28,4 +28,5 @@ void MockPermissionController::ResetPermission(blink::PermissionType permission, void MockPermissionController::UnsubscribePermissionStatusChange( SubscriptionId subscription_id) {} + } // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 17f7e37a394a38d8fd93c3a2de3a5a9a1e71f1b7..bc135366a2b5c8ec81e2870f5910de72a7959360 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -1628,6 +1628,7 @@ test("content_browsertests") { "//components/network_session_configurator/common", "//components/payments/mojom", "//components/performance_manager", + "//components/permissions:test_support", "//components/services/quarantine:test_support", "//components/services/storage", "//components/services/storage/public/cpp",