| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <optional> |
| |
| #include "base/strings/strcat.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/media/webrtc/media_stream_device_permission_context.h" |
| #include "chrome/browser/permissions/permission_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_content_scrim_view.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/permissions/permission_manager.h" |
| #include "components/permissions/permission_request_manager.h" |
| #include "components/permissions/test/mock_permission_request.h" |
| #include "components/permissions/test/permission_request_observer.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/render_frame_host_test_support.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "third_party/blink/public/common/features_generated.h" |
| #include "third_party/blink/public/mojom/permissions/permission.mojom-shared.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/views/widget/any_widget_observer.h" |
| |
| namespace { |
| |
| // Simulates a click on an element with the given |id|. |
| void ClickElementWithId(content::WebContents* web_contents, |
| const std::string& id) { |
| ASSERT_TRUE( |
| content::ExecJs(web_contents, content::JsReplace("clickById($1)", id))); |
| } |
| |
| } // namespace |
| |
| class PermissionElementBrowserTestBase : public InProcessBrowserTest { |
| public: |
| PermissionElementBrowserTestBase() = default; |
| |
| PermissionElementBrowserTestBase(const PermissionElementBrowserTestBase&) = |
| delete; |
| PermissionElementBrowserTestBase& operator=( |
| const PermissionElementBrowserTestBase&) = delete; |
| |
| ~PermissionElementBrowserTestBase() override = default; |
| |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| console_observer_ = |
| std::make_unique<content::WebContentsConsoleObserver>(web_contents()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( |
| browser(), |
| embedded_test_server()->GetURL("/permissions/permission_element.html"), |
| 1)); |
| } |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetWebContentsAt(0); |
| } |
| |
| void WaitForResolveEvent(const std::string& id) { |
| ExpectConsoleMessage(id + "-promptaction"); |
| ExpectConsoleMessage(id + "-resolve"); |
| } |
| |
| void WaitForUpdateGrantedPermissionElement(const std::string& id) { |
| ExpectConsoleMessage(id + "-granted"); |
| } |
| |
| void WaitForDismissEvent(const std::string& id) { |
| ExpectConsoleMessage(id + "-promptdismiss"); |
| ExpectConsoleMessage(id + "-dismiss"); |
| } |
| |
| void ExpectNoEvents() { EXPECT_EQ(0u, console_observer_->messages().size()); } |
| |
| void ExpectConsoleMessage(const std::string& expected_message, |
| std::optional<blink::mojom::ConsoleMessageLevel> |
| log_level = std::nullopt) { |
| EXPECT_TRUE(console_observer_->Wait()); |
| |
| EXPECT_EQ(1u, console_observer_->messages().size()); |
| EXPECT_EQ(expected_message, console_observer_->GetMessageAt(0)); |
| if (log_level) { |
| EXPECT_EQ(log_level.value(), console_observer_->messages()[0].log_level); |
| } |
| |
| // WebContentsConsoleObserver::Wait() will only wait until there is at least |
| // one message. We need to reset the |console_observer_| in order to be able |
| // to wait for the next message. |
| console_observer_ = |
| std::make_unique<content::WebContentsConsoleObserver>(web_contents()); |
| } |
| |
| void SkipInvalidElementMessage() { |
| ExpectConsoleMessage( |
| "The permission type 'invalid microphone' is not supported by the " |
| "permission element."); |
| } |
| |
| void TestPromptPosition( |
| permissions::feature_params::PermissionElementPromptPosition position) { |
| auto* permission_request_manager = |
| permissions::PermissionRequestManager::FromWebContents(web_contents()); |
| |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), "camera"); |
| observer.Wait(); |
| |
| EXPECT_EQ( |
| permission_request_manager->GetCurrentPrompt()->GetPromptPosition(), |
| position); |
| |
| permission_request_manager->Dismiss(); |
| permission_request_manager->FinalizeCurrentRequests(); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| std::unique_ptr<content::WebContentsConsoleObserver> console_observer_; |
| }; |
| |
| class PermissionElementBrowserTest : public PermissionElementBrowserTestBase { |
| public: |
| PermissionElementBrowserTest() { |
| feature_list_.InitWithFeatures( |
| {blink::features::kPermissionElement, |
| blink::features::kBypassPepcSecurityForTesting}, |
| {permissions::features::kPermissionElementPromptPositioning}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| RequestInvalidPermissionType) { |
| ExpectConsoleMessage( |
| "The permission type 'invalid microphone' is not supported by the " |
| "permission element.", |
| blink::mojom::ConsoleMessageLevel::kError); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| RequestPermissionDispatchResolveEvent) { |
| SkipInvalidElementMessage(); |
| |
| permissions::PermissionRequestManager::AutoResponseType responses[] = { |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL, |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE, |
| permissions::PermissionRequestManager::AutoResponseType::DENY_ALL}; |
| |
| std::string permission_ids[] = {"geolocation", "microphone", "camera", |
| "camera-microphone"}; |
| |
| for (const auto& response : responses) { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test(response); |
| for (const auto& id : permission_ids) { |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| WaitForResolveEvent(id); |
| } |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| DispatchResolveEventUpdateGrantedElement) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL); |
| std::string permission_ids[] = {"geolocation", "microphone", "camera", |
| "camera-microphone"}; |
| for (const auto& id : permission_ids) { |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| WaitForResolveEvent(id); |
| ASSERT_TRUE(content::ExecJs( |
| web_contents(), content::JsReplace("notifyWhenGranted($1);", id))); |
| WaitForUpdateGrantedPermissionElement(id); |
| } |
| } |
| |
| class PermissionServiceInterceptor : public blink::mojom::PermissionObserver { |
| public: |
| explicit PermissionServiceInterceptor( |
| content::RenderFrameHost* render_frame_host) |
| : render_frame_host_(render_frame_host) { |
| OverrideBinderForTesting(); |
| } |
| |
| ~PermissionServiceInterceptor() override = default; |
| |
| blink::mojom::PermissionService* GetForwardingInterface() { |
| return permission_service_.get(); |
| } |
| |
| void AddPermissionStatusObserver(blink::mojom::PermissionName permission) { |
| auto descriptor = blink::mojom::PermissionDescriptor::New(); |
| descriptor->name = permission; |
| GetForwardingInterface()->AddCombinedPermissionObserver( |
| std::move(descriptor), blink::mojom::PermissionStatus::ASK, |
| GetRemote()); |
| } |
| |
| // Blocks until getting status granted event. |
| void WaitForPermissionGranted() { loop_.Run(); } |
| |
| private: |
| mojo::PendingRemote<blink::mojom::PermissionObserver> GetRemote() { |
| mojo::PendingRemote<blink::mojom::PermissionObserver> remote; |
| observer_receiver_.Bind(remote.InitWithNewPipeAndPassReceiver()); |
| return remote; |
| } |
| |
| // blink::mojom::PermissionObserver implementation. |
| void OnPermissionStatusChange( |
| blink::mojom::PermissionStatus status) override { |
| if (status == blink::mojom::PermissionStatus::GRANTED) { |
| loop_.Quit(); |
| } |
| } |
| |
| void OverrideBinderForTesting() { |
| content::CreatePermissionService( |
| render_frame_host_, permission_service_.BindNewPipeAndPassReceiver()); |
| } |
| |
| base::RunLoop loop_; |
| const raw_ptr<content::RenderFrameHost> render_frame_host_; |
| mojo::Remote<blink::mojom::PermissionService> permission_service_; |
| mojo::Receiver<blink::mojom::PermissionObserver> observer_receiver_{this}; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| CombinedPermissionAndDeviceStatusesGranted) { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL); |
| PermissionServiceInterceptor permission_service( |
| web_contents()->GetPrimaryMainFrame()); |
| MediaStreamDevicePermissionContext* camera_permission_context = |
| static_cast<MediaStreamDevicePermissionContext*>( |
| PermissionManagerFactory::GetForProfile(browser()->profile()) |
| ->GetPermissionContextForTesting( |
| ContentSettingsType::MEDIASTREAM_CAMERA)); |
| camera_permission_context->set_has_device_permission_for_test( |
| /*has_permission=*/false); |
| permission_service.AddPermissionStatusObserver( |
| blink::mojom::PermissionName::VIDEO_CAPTURE); |
| ClickElementWithId(web_contents(), "camera"); |
| // Simulate that we accept the device permission request. |
| camera_permission_context->set_has_device_permission_for_test( |
| /*has_permission=*/true); |
| permission_service.WaitForPermissionGranted(); |
| camera_permission_context->set_has_device_permission_for_test(std::nullopt); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| RequestPermissionDispatchDismissEvent) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::DISMISS); |
| std::string permission_ids[] = {"geolocation", "microphone", "camera", |
| "camera-microphone"}; |
| for (const auto& id : permission_ids) { |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| WaitForDismissEvent(id); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| ClickingScrimViewDispatchDismissEvent) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::NONE); |
| std::string permission_ids[] = {"geolocation", "microphone", "camera", |
| "camera-microphone"}; |
| for (const auto& id : permission_ids) { |
| views::NamedWidgetShownWaiter waiter( |
| views::test::AnyWidgetTestPasskey{}, |
| "EmbeddedPermissionPromptContentScrimWidget"); |
| ClickElementWithId(web_contents(), id); |
| auto* scrim_view = static_cast<EmbeddedPermissionPromptContentScrimView*>( |
| waiter.WaitIfNeededAndGet()->GetContentsView()); |
| scrim_view->OnMousePressed( |
| ui::MouseEvent(ui::EventType::kMousePressed, gfx::Point(), gfx::Point(), |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0)); |
| WaitForDismissEvent(id); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| TappingScrimViewDispatchDismissEvent) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::NONE); |
| std::string permission_ids[] = {"geolocation", "microphone", "camera", |
| "camera-microphone"}; |
| for (const auto& id : permission_ids) { |
| views::NamedWidgetShownWaiter waiter( |
| views::test::AnyWidgetTestPasskey{}, |
| "EmbeddedPermissionPromptContentScrimWidget"); |
| ClickElementWithId(web_contents(), id); |
| auto* scrim_view = static_cast<EmbeddedPermissionPromptContentScrimView*>( |
| waiter.WaitIfNeededAndGet()->GetContentsView()); |
| ui::GestureEvent tap_down( |
| gfx::Point().x(), gfx::Point().y(), 0, base::TimeTicks::Now(), |
| ui::GestureEventDetails(ui::EventType::kGestureTapDown)); |
| scrim_view->OnGestureEvent(&tap_down); |
| ui::GestureEvent tap_up( |
| gfx::Point().x(), gfx::Point().y(), 0, base::TimeTicks::Now(), |
| ui::GestureEventDetails(ui::EventType::kGestureTap)); |
| scrim_view->OnGestureEvent(&tap_up); |
| WaitForDismissEvent(id); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, TabSwitchingClosesPrompt) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::NONE); |
| |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), "camera"); |
| observer.Wait(); |
| |
| std::unique_ptr<content::WebContents> new_tab = content::WebContents::Create( |
| content::WebContents::CreateParams(browser()->profile())); |
| browser()->tab_strip_model()->AppendWebContents(std::move(new_tab), |
| /*foreground*/ false); |
| |
| ExpectNoEvents(); |
| browser()->tab_strip_model()->ActivateTabAt(1); |
| WaitForDismissEvent("camera"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, |
| DoubleClickDoesNotTriggerTwoRequests) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::DISMISS); |
| |
| permissions::PermissionRequestObserver observer1(web_contents()); |
| content::WebContentsConsoleObserver console_observer(web_contents()); |
| |
| // Click the element twice. |
| ClickElementWithId(web_contents(), "microphone"); |
| ClickElementWithId(web_contents(), "microphone"); |
| |
| EXPECT_EQ(console_observer.messages().size(), 1u); |
| ExpectConsoleMessage( |
| "The permission element already has a request in progress."); |
| |
| // Multiple clicks on the same permission element should only trigger one |
| // request. |
| observer1.Wait(); |
| EXPECT_TRUE(observer1.request_shown()); |
| WaitForDismissEvent("microphone"); |
| |
| // Verify that no duplicate "microphone" requests or dismiss events are |
| // created. |
| permissions::PermissionRequestObserver observer2(web_contents()); |
| ClickElementWithId(web_contents(), "camera"); |
| observer2.Wait(); |
| EXPECT_TRUE(observer2.request_shown()); |
| WaitForDismissEvent("camera"); |
| |
| // Verify that clicking again on the same element after the prompt was |
| // dismissed, results in a permission request being shown. |
| permissions::PermissionRequestObserver observer3(web_contents()); |
| ClickElementWithId(web_contents(), "microphone"); |
| observer3.Wait(); |
| WaitForDismissEvent("microphone"); |
| EXPECT_TRUE(observer3.request_shown()); |
| } |
| |
| class PermissionElementWithSecurityBrowserTest |
| : public PermissionElementBrowserTestBase { |
| public: |
| PermissionElementWithSecurityBrowserTest() { |
| feature_list_.InitWithFeatures({blink::features::kPermissionElement}, {}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementWithSecurityBrowserTest, |
| JsClickingDisabledWithoutFeature) { |
| permissions::PermissionRequestObserver permission_observer(web_contents()); |
| content::WebContentsConsoleObserver console_observer(web_contents()); |
| |
| // Clicking via JS should be disabled. |
| ClickElementWithId(web_contents(), "microphone"); |
| ASSERT_TRUE(console_observer.Wait()); |
| EXPECT_EQ(console_observer.messages().size(), 1u); |
| EXPECT_EQ( |
| console_observer.GetMessageAt(0u), |
| "The permission element can only be activated by actual user clicks."); |
| EXPECT_FALSE(permission_observer.request_shown()); |
| |
| // Also attempt clicking by creating a MouseEvent. |
| ASSERT_TRUE(content::ExecJs( |
| web_contents(), |
| content::JsReplace("document.getElementById($1).dispatchEvent(new " |
| "MouseEvent('click'));", |
| "microphone"))); |
| |
| ASSERT_TRUE(console_observer.Wait()); |
| EXPECT_EQ(console_observer.messages().size(), 2u); |
| EXPECT_EQ( |
| console_observer.GetMessageAt(1u), |
| "The permission element can only be activated by actual user clicks."); |
| EXPECT_FALSE(permission_observer.request_shown()); |
| |
| // Now generate a legacy microphone permission request and wait until it is |
| // observed. Then verify that no other requests have arrived. |
| ASSERT_TRUE(content::ExecJs( |
| web_contents(), |
| "const stream = navigator.mediaDevices.getUserMedia({audio: true});")); |
| permission_observer.Wait(); |
| EXPECT_TRUE(permission_observer.request_shown()); |
| EXPECT_EQ(console_observer.messages().size(), 2u); |
| |
| // Verify that we have observed the non-PEPC initiated request. |
| EXPECT_EQ( |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->Requests() |
| .size(), |
| 1U); |
| EXPECT_FALSE( |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->Requests()[0] |
| ->IsEmbeddedPermissionElementInitiated()); |
| } |
| |
| class PermissionElementStandardizedBrowserZoomTest |
| : public PermissionElementBrowserTestBase, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| PermissionElementStandardizedBrowserZoomTest() { |
| // Also enable/disable the StandardizedBrowserZoom feature. |
| if (GetParam()) { |
| feature_list_.InitWithFeatures( |
| {blink::features::kPermissionElement, |
| blink::features::kBypassPepcSecurityForTesting, |
| blink::features::kStandardizedBrowserZoom}, |
| {}); |
| } else { |
| feature_list_.InitWithFeatures( |
| {blink::features::kPermissionElement, |
| blink::features::kBypassPepcSecurityForTesting}, |
| {blink::features::kStandardizedBrowserZoom}); |
| } |
| } |
| |
| void WaitForFontSizeTooLargeEvent(const std::string& id) { |
| auto type_attribute_value = content::EvalJs( |
| web_contents(), |
| content::JsReplace("document.getElementById($1).type", id)); |
| EXPECT_TRUE(type_attribute_value.is_ok()); |
| ExpectConsoleMessage("Font size of the permission element '" + |
| type_attribute_value.ExtractString() + |
| "' is too large"); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(PermissionElementStandardizedBrowserZoomTest, |
| BrowserZoomDoesNotAffectValidation) { |
| SkipInvalidElementMessage(); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL); |
| zoom::ZoomController* zoom_controller = |
| zoom::ZoomController::FromWebContents(web_contents()); |
| |
| // 2x zoom is enough since the font-size is already set to xxx-large which is |
| // the upper bound. |
| zoom_controller->SetZoomLevel(2); |
| |
| for (const auto& id : |
| {"geolocation", "camera", "microphone", "camera-microphone"}) { |
| // The permission element still works. |
| ClickElementWithId(web_contents(), id); |
| WaitForResolveEvent(id); |
| ExpectNoEvents(); |
| |
| // Now set the CSS "zoom" to 2x. |
| ASSERT_TRUE(content::ExecJs( |
| web_contents(), |
| content::JsReplace("document.getElementById($1).style.zoom = 2;", id))); |
| WaitForFontSizeTooLargeEvent(id); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| PermissionElementStandardizedBrowserZoomTest, |
| testing::Bool()); |
| |
| class PermissionElementNearElementBrowserTest |
| : public PermissionElementBrowserTestBase { |
| public: |
| PermissionElementNearElementBrowserTest() { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{blink::features::kPermissionElement, {}}, |
| {blink::features::kBypassPepcSecurityForTesting, {}}, |
| {permissions::features::kPermissionElementPromptPositioning, |
| {{"PermissionElementPromptPositioningParam", "near_element"}}}}, |
| {}); |
| } |
| }; |
| |
| class PermissionElementWindowMiddleBrowserTest |
| : public PermissionElementBrowserTestBase { |
| public: |
| PermissionElementWindowMiddleBrowserTest() { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{blink::features::kPermissionElement, {}}, |
| {blink::features::kBypassPepcSecurityForTesting, {}}, |
| {permissions::features::kPermissionElementPromptPositioning, |
| {{"PermissionElementPromptPositioningParam", "window_middle"}}}}, |
| {}); |
| } |
| }; |
| |
| class PermissionElementLegacyPromptBrowserTest |
| : public PermissionElementBrowserTestBase { |
| public: |
| PermissionElementLegacyPromptBrowserTest() { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{blink::features::kPermissionElement, {}}, |
| {blink::features::kBypassPepcSecurityForTesting, {}}, |
| {permissions::features::kPermissionElementPromptPositioning, |
| {{"PermissionElementPromptPositioningParam", "legacy_prompt"}}}}, |
| {}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest, DefaultPromptPosition) { |
| TestPromptPosition(permissions::feature_params:: |
| PermissionElementPromptPosition::kWindowMiddle); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementNearElementBrowserTest, |
| PromptPosition) { |
| TestPromptPosition(permissions::feature_params:: |
| PermissionElementPromptPosition::kNearElement); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementWindowMiddleBrowserTest, |
| PromptPosition) { |
| TestPromptPosition(permissions::feature_params:: |
| PermissionElementPromptPosition::kWindowMiddle); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PermissionElementLegacyPromptBrowserTest, |
| PromptPosition) { |
| TestPromptPosition(permissions::feature_params:: |
| PermissionElementPromptPosition::kLegacyPrompt); |
| } |
| |
| // This text fixture does not navigate to any particular URL by default, the |
| // tests instead decide which URL to navigate the page to. |
| class MiscellaneousElementBrowserTest |
| : public PermissionElementBrowserTestBase { |
| public: |
| MiscellaneousElementBrowserTest() { |
| feature_list_.InitWithFeatures( |
| {blink::features::kPermissionElement, |
| blink::features::kBypassPepcSecurityForTesting}, |
| {permissions::features::kPermissionElementPromptPositioning}); |
| } |
| |
| void SetUpOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| console_observer_ = |
| std::make_unique<content::WebContentsConsoleObserver>(web_contents()); |
| } |
| |
| void NavigateToURL(const std::string& url) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( |
| browser(), embedded_test_server()->GetURL(url), 1)); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(MiscellaneousElementBrowserTest, |
| EventContentAttributes) { |
| NavigateToURL("/permissions/permission_element_events_tester.html"); |
| const char* id = "microphone"; |
| { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::DISMISS); |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| WaitForDismissEvent(id); |
| } |
| |
| { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test(permissions::PermissionRequestManager:: |
| AutoResponseType::ACCEPT_ALL); |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| WaitForResolveEvent(id); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(MiscellaneousElementBrowserTest, |
| EventsBubbleAndAreCancelable) { |
| NavigateToURL("/permissions/permission_element_events_tester.html"); |
| const char* id = "camera"; |
| |
| { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::DISMISS); |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| |
| // The event is reported by the parent element, then the grandparent |
| // element. |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-promptdismiss"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-promptdismiss"})); |
| ExpectConsoleMessage( |
| base::StrCat({"grandparent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-dismiss"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-dismiss"})); |
| ExpectConsoleMessage( |
| base::StrCat({"grandparent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-bubbles-true"})); |
| } |
| |
| { |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test(permissions::PermissionRequestManager:: |
| AutoResponseType::ACCEPT_ALL); |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| |
| // The event is reported by the parent element, then the grandparent |
| // element. |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-promptaction"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-promptaction"})); |
| ExpectConsoleMessage( |
| base::StrCat({"grandparent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-resolve"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"parent-", id, "-bubbles-true"})); |
| |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-resolve"})); |
| ExpectConsoleMessage( |
| base::StrCat({"grandparent-", id, "-cancelable-true"})); |
| ExpectConsoleMessage(base::StrCat({"grandparent-", id, "-bubbles-true"})); |
| } |
| } |
| |
| // Test crash reported in crbug.com/374034614, caused by a race condition |
| // between HtmlPermissionElement::OnEmbeddedPermissionsDecided and |
| // HtmlPermissionElement::OnPermissionStatusChange. |
| IN_PROC_BROWSER_TEST_F(MiscellaneousElementBrowserTest, |
| CrashWhenElementHidesOnGrant) { |
| NavigateToURL("/permissions/permission_element_hide_when_granted.html"); |
| permissions::PermissionRequestManager::FromWebContents(web_contents()) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL); |
| HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile( |
| Profile::FromBrowserContext(web_contents()->GetBrowserContext())); |
| |
| // The original crash is a race condition that seems to reproduce about half |
| // the time. Therefore we run this multiple times and with multiple elements |
| // to ensure the chance of this test passing is minimal if the crash root |
| // cause is not fixed. |
| int test_runs = 5; |
| while (test_runs--) { |
| for (const auto& id : {"camera", "microphone", "geolocation"}) { |
| permissions::PermissionRequestObserver observer(web_contents()); |
| ClickElementWithId(web_contents(), id); |
| observer.Wait(); |
| } |
| |
| map->SetContentSettingDefaultScope( |
| embedded_test_server()->base_url(), embedded_test_server()->base_url(), |
| ContentSettingsType::MEDIASTREAM_CAMERA, CONTENT_SETTING_DEFAULT); |
| map->SetContentSettingDefaultScope( |
| embedded_test_server()->base_url(), embedded_test_server()->base_url(), |
| ContentSettingsType::MEDIASTREAM_MIC, CONTENT_SETTING_DEFAULT); |
| map->SetContentSettingDefaultScope( |
| embedded_test_server()->base_url(), embedded_test_server()->base_url(), |
| ContentSettingsType::GEOLOCATION, CONTENT_SETTING_DEFAULT); |
| } |
| } |