| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" |
| |
| #include "base/json/json_reader.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "chrome/browser/extensions/chrome_test_extension_loader.h" |
| #include "chrome/browser/extensions/extension_action_runner.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" |
| #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h" |
| #include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "extensions/browser/pref_names.h" |
| #include "extensions/browser/test_extension_registry_observer.h" |
| #include "extensions/common/extension_features.h" |
| #include "extensions/common/extension_id.h" |
| #include "extensions/test/permissions_manager_waiter.h" |
| #include "extensions/test/test_extension_dir.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/base/dragdrop/drag_drop_types.h" |
| #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h" |
| |
| namespace { |
| |
| using SitePermissionsHelper = extensions::SitePermissionsHelper; |
| using PermissionsManager = extensions::PermissionsManager; |
| |
| // TODO(crbug.com/40916158): Same as permission's ChipController. Pull out to a |
| // shared location. |
| base::TimeDelta kConfirmationDisplayDuration = base::Seconds(4); |
| |
| // A scoper that manages a Browser instance created by BrowserWithTestWindowTest |
| // beyond the default instance it creates in SetUp. |
| class AdditionalBrowser { |
| public: |
| explicit AdditionalBrowser(std::unique_ptr<Browser> browser) |
| : browser_(std::move(browser)), |
| browser_view_(BrowserView::GetBrowserViewForBrowser(browser_.get())) {} |
| |
| ~AdditionalBrowser() { |
| // Tear down `browser_`, similar to TestWithBrowserView::TearDown. |
| browser_.release(); |
| browser_view_->GetWidget()->CloseNow(); |
| } |
| |
| ExtensionsToolbarContainer* extensions_container() { |
| return browser_view_->toolbar()->extensions_container(); |
| } |
| |
| private: |
| std::unique_ptr<Browser> browser_; |
| raw_ptr<BrowserView, DanglingUntriaged> browser_view_; |
| }; |
| |
| } // namespace |
| |
| class ExtensionsToolbarContainerUnitTest : public ExtensionsToolbarUnitTest { |
| public: |
| ExtensionsToolbarContainerUnitTest(); |
| ~ExtensionsToolbarContainerUnitTest() override = default; |
| ExtensionsToolbarContainerUnitTest( |
| const ExtensionsToolbarContainerUnitTest&) = delete; |
| ExtensionsToolbarContainerUnitTest& operator=( |
| const ExtensionsToolbarContainerUnitTest&) = delete; |
| |
| // Navigates to `url`. |
| void NavigateAndCommit(const GURL& URL); |
| |
| // Returns the view of the given `extension_id` if the extension is currently |
| // pinned. |
| ToolbarActionView* GetPinnedExtensionView( |
| const extensions::ExtensionId& extension_id); |
| |
| // Returns whether the request access button is visible or not. |
| bool IsRequestAccessButtonVisible(); |
| |
| // ExtensionsToolbarUnitTest: |
| void SetUp() override; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| raw_ptr<content::WebContentsTester, DanglingUntriaged> web_contents_tester_; |
| }; |
| |
| ExtensionsToolbarContainerUnitTest::ExtensionsToolbarContainerUnitTest() |
| : ExtensionsToolbarUnitTest( |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME) { |
| scoped_feature_list_.InitAndEnableFeature( |
| extensions_features::kExtensionsMenuAccessControl); |
| } |
| |
| void ExtensionsToolbarContainerUnitTest::NavigateAndCommit(const GURL& url) { |
| web_contents_tester_->NavigateAndCommit(url); |
| WaitForAnimation(); |
| } |
| |
| ToolbarActionView* ExtensionsToolbarContainerUnitTest::GetPinnedExtensionView( |
| const extensions::ExtensionId& extension_id) { |
| std::vector<ToolbarActionView*> actions = GetPinnedExtensionViews(); |
| auto it = |
| base::ranges::find(actions, extension_id, [](ToolbarActionView* action) { |
| return action->view_controller()->GetId(); |
| }); |
| if (it == actions.end()) |
| return nullptr; |
| return *it; |
| } |
| |
| bool ExtensionsToolbarContainerUnitTest::IsRequestAccessButtonVisible() { |
| return request_access_button()->GetVisible(); |
| } |
| |
| void ExtensionsToolbarContainerUnitTest::SetUp() { |
| ExtensionsToolbarUnitTest::SetUp(); |
| web_contents_tester_ = AddWebContentsAndGetTester(); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, ReorderPinnedExtensions) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| // Verify the order is A, B, C. |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| |
| // Simulate dragging extension C to the first slot. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id()); |
| EXPECT_TRUE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| ui::OSExchangeData drag_data; |
| extensions_container()->WriteDragDataForView(drag_view, gfx::Point(), |
| &drag_data); |
| gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin()); |
| ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point, |
| ui::DragDropTypes::DRAG_MOVE); |
| extensions_container()->OnDragUpdated(drop_event); |
| auto drop_cb = extensions_container()->GetDropCallback(drop_event); |
| ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone; |
| std::move(drop_cb).Run(drop_event, output_drag_op, |
| /*drag_image_layer_owner=*/nullptr); |
| WaitForAnimation(); |
| |
| // Verify the new order is C, A, B. |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName)); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, ForcePinnedExtensionsCannotReorder) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| // Make Extension C force-pinned, as if it was controlled by the |
| // ExtensionSettings policy. |
| std::string json = base::StringPrintf( |
| R"({ |
| "%s": { |
| "toolbar_pin": "force_pinned" |
| } |
| })", |
| extensionC->id().c_str()); |
| std::optional<base::Value> settings = base::JSONReader::Read(json); |
| ASSERT_TRUE(settings.has_value()); |
| profile()->GetTestingPrefService()->SetManagedPref( |
| extensions::pref_names::kExtensionManagement, |
| base::Value::ToUniquePtrValue(std::move(settings.value()))); |
| |
| // Verify the order is A, B, C. |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| EXPECT_TRUE(toolbar_model->IsActionForcePinned(extensionC->id())); |
| |
| // Force-pinned extension should not be draggable. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id()); |
| EXPECT_FALSE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| } |
| |
| // Tests that when an extension is reloaded it remains visible in the toolbar. |
| TEST_F(ExtensionsToolbarContainerUnitTest, ReloadExtensionKeepsPinnedState) { |
| // The extension must have a manifest to be reloaded. |
| extensions::TestExtensionDir extension_directory; |
| constexpr char kManifest[] = R"({ |
| "name": "Test Extension", |
| "version": "1", |
| "manifest_version": 3 |
| })"; |
| extension_directory.WriteManifest(kManifest); |
| extensions::ChromeTestExtensionLoader loader(profile()); |
| scoped_refptr<const extensions::Extension> extension = |
| loader.LoadExtension(extension_directory.UnpackedPath()); |
| |
| // By default, extension on installation is unpinned. |
| EXPECT_FALSE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| |
| // Pin extension and verify it is visible on the toolbar. |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| toolbar_model->SetActionVisibility(extension->id(), true); |
| EXPECT_TRUE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| |
| // Reload the extension. |
| extensions::TestExtensionRegistryObserver registry_observer( |
| extensions::ExtensionRegistry::Get(profile())); |
| ReloadExtension(extension->id()); |
| ASSERT_TRUE(registry_observer.WaitForExtensionLoaded()); |
| WaitForAnimation(); |
| |
| // Verify the extension is visible on the toolbar. |
| EXPECT_TRUE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| } |
| |
| // Tests that a when an extension is reloaded with manifest errors, and |
| // therefore fails to be loaded into Chrome, it's removed from the toolbar. |
| TEST_F(ExtensionsToolbarContainerUnitTest, ReloadExtensionFailed) { |
| extensions::TestExtensionDir extension_directory; |
| constexpr char kManifest[] = R"({ |
| "name": "Test Extension", |
| "version": "1", |
| "manifest_version": 3 |
| })"; |
| extension_directory.WriteManifest(kManifest); |
| extensions::ChromeTestExtensionLoader loader(profile()); |
| scoped_refptr<const extensions::Extension> extension = |
| loader.LoadExtension(extension_directory.UnpackedPath()); |
| |
| // By default, extension on installation is unpinned. |
| EXPECT_FALSE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| |
| // Pin extension and verify it is visible on the toolbar. |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| toolbar_model->SetActionVisibility(extension->id(), true); |
| EXPECT_TRUE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| |
| // Replace the extension's valid manifest with one containing errors. In this |
| // case, 'version' keys is missing. |
| constexpr char kManifestWithErrors[] = R"({ |
| "name": "Test", |
| "manifest_version": 3, |
| })"; |
| extension_directory.WriteManifest(kManifestWithErrors); |
| |
| // Reload the extension. It should fail due to the manifest errors. |
| extension_service()->ReloadExtensionWithQuietFailure(extension->id()); |
| base::RunLoop().RunUntilIdle(); |
| WaitForAnimation(); |
| |
| // Verify the extension is no longer visible on the toolbar. |
| EXPECT_FALSE( |
| extensions_container()->IsActionVisibleOnToolbar(extension->id())); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| PinnedExtensionAppearsInAnotherWindow) { |
| const std::string& extension_id = InstallExtension("Extension")->id(); |
| |
| AdditionalBrowser browser2( |
| CreateBrowser(browser()->profile(), browser()->type(), |
| /* hosted_app */ false, /* browser_window */ nullptr)); |
| |
| // Verify extension is unpinned in both windows. |
| EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id)); |
| EXPECT_FALSE( |
| browser2.extensions_container()->IsActionVisibleOnToolbar(extension_id)); |
| |
| // Pin extension in one window. |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| toolbar_model->SetActionVisibility(extension_id, true); |
| |
| // Both windows open get the pinned extension. |
| EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(extension_id)); |
| EXPECT_TRUE( |
| browser2.extensions_container()->IsActionVisibleOnToolbar(extension_id)); |
| |
| AdditionalBrowser browser3( |
| CreateBrowser(browser()->profile(), browser()->type(), |
| /* hosted_app */ false, /* browser_window */ nullptr)); |
| |
| // Brand-new window also gets the pinned extension. |
| EXPECT_TRUE( |
| browser3.extensions_container()->IsActionVisibleOnToolbar(extension_id)); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| PinnedExtensionsReorderOnPrefChange) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| // Verify the order is A, B, C. |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| |
| // Set the order using prefs. |
| extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions( |
| {extensionB->id(), extensionC->id(), extensionA->id()}); |
| WaitForAnimation(); |
| |
| // Verify the new order is B, C, A. |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionBName, kExtensionCName, kExtensionAName)); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, RunDropCallback) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| |
| // Simulate dragging extension C to the first slot. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id()); |
| EXPECT_TRUE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| ui::OSExchangeData drag_data; |
| extensions_container()->WriteDragDataForView(drag_view, gfx::Point(), |
| &drag_data); |
| gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin()); |
| ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point, |
| ui::DragDropTypes::DRAG_MOVE); |
| extensions_container()->OnDragUpdated(drop_event); |
| auto cb = extensions_container()->GetDropCallback(drop_event); |
| ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone; |
| std::move(cb).Run(drop_event, output_drag_op, |
| /*drag_image_layer_owner=*/nullptr); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName)); |
| EXPECT_EQ(output_drag_op, ui::mojom::DragOperation::kMove); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, ResetDropCallback) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| |
| // Simulate dragging "C Extension" to the first slot. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionC->id()); |
| EXPECT_TRUE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| ui::OSExchangeData drag_data; |
| extensions_container()->WriteDragDataForView(drag_view, gfx::Point(), |
| &drag_data); |
| gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin()); |
| ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point, |
| ui::DragDropTypes::DRAG_MOVE); |
| extensions_container()->OnDragUpdated(drop_event); |
| auto cb = extensions_container()->GetDropCallback(drop_event); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionCName, kExtensionAName, kExtensionBName)); |
| |
| // If the drop callback is reset (and never invoked), the drag should be |
| // aborted, and items should be back in their original order. |
| cb.Reset(); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| InvalidateDropCallbackOnActionAdded) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| WaitForAnimation(); |
| |
| EXPECT_THAT(GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName)); |
| |
| // Simulate dragging extension B to the first slot. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionB->id()); |
| EXPECT_TRUE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| ui::OSExchangeData drag_data; |
| extensions_container()->WriteDragDataForView(drag_view, gfx::Point(), |
| &drag_data); |
| gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin()); |
| ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point, |
| ui::DragDropTypes::DRAG_MOVE); |
| extensions_container()->OnDragUpdated(drop_event); |
| auto cb = extensions_container()->GetDropCallback(drop_event); |
| WaitForAnimation(); |
| |
| EXPECT_THAT(GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionBName, kExtensionAName)); |
| |
| constexpr char kExtensionCName[] = "C Extension"; |
| auto extensionC = InstallExtension(kExtensionCName); |
| toolbar_model->SetActionVisibility(extensionC->id(), true); |
| WaitForAnimation(); |
| |
| // The drop callback should be invalidated, and items should be back in their |
| // original order. |
| ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone; |
| std::move(cb).Run(drop_event, output_drag_op, |
| /*drag_image_layer_owner=*/nullptr); |
| WaitForAnimation(); |
| |
| EXPECT_THAT( |
| GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName, kExtensionCName)); |
| } |
| |
| // ToolbarActionsModel::MovePinnedAction crashes if pinned extensions changes |
| // while the drop callback isn't invalidated. This test makes sure this doesn't |
| // happen anymore. https://crbug.com/1268239. |
| TEST_F(ExtensionsToolbarContainerUnitTest, InvalidateDropCallbackOnPrefChange) { |
| constexpr char kExtensionAName[] = "A Extension"; |
| auto extensionA = InstallExtension(kExtensionAName); |
| constexpr char kExtensionBName[] = "B Extension"; |
| auto extensionB = InstallExtension(kExtensionBName); |
| |
| auto* toolbar_model = ToolbarActionsModel::Get(profile()); |
| ASSERT_TRUE(toolbar_model); |
| |
| toolbar_model->SetActionVisibility(extensionA->id(), true); |
| toolbar_model->SetActionVisibility(extensionB->id(), true); |
| WaitForAnimation(); |
| |
| EXPECT_THAT(GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionAName, kExtensionBName)); |
| |
| // Simulate dragging extension B to the first slot. |
| ToolbarActionView* drag_view = GetPinnedExtensionView(extensionB->id()); |
| EXPECT_TRUE(extensions_container()->CanStartDragForView( |
| drag_view, gfx::Point(), gfx::Point())); |
| ui::OSExchangeData drag_data; |
| extensions_container()->WriteDragDataForView(drag_view, gfx::Point(), |
| &drag_data); |
| gfx::PointF drop_point(GetPinnedExtensionView(extensionA->id())->origin()); |
| ui::DropTargetEvent drop_event(drag_data, drop_point, drop_point, |
| ui::DragDropTypes::DRAG_MOVE); |
| extensions_container()->OnDragUpdated(drop_event); |
| auto cb = extensions_container()->GetDropCallback(drop_event); |
| WaitForAnimation(); |
| |
| EXPECT_THAT(GetPinnedExtensionNames(), |
| testing::ElementsAre(kExtensionBName, kExtensionAName)); |
| |
| extensions::ExtensionPrefs::Get(profile())->SetPinnedExtensions({}); |
| WaitForAnimation(); |
| |
| // The drop callback should be invalidated, and items should be back in their |
| // original order. |
| ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone; |
| std::move(cb).Run(drop_event, output_drag_op, |
| /*drag_image_layer_owner=*/nullptr); |
| WaitForAnimation(); |
| |
| EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre()); |
| } |
| |
| // Test that the extension button state changes after site permissions updates. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| ExtensionsButton_SitePermissionsUpdates) { |
| // Install an extension that requests host permissions. |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| |
| const GURL url("http://www.url.com"); |
| auto url_origin = url::Origin::Create(url); |
| NavigateAndCommit(url); |
| |
| auto* manager = extensions::PermissionsManager::Get(profile()); |
| { |
| // Extensions button has "all extensions blocked" icon type when it's |
| // an user restricted site. |
| extensions::PermissionsManagerWaiter manager_waiter(manager); |
| manager->AddUserRestrictedSite(url_origin); |
| manager_waiter.WaitForUserPermissionsSettingsChange(); |
| WaitForAnimation(); |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kAllExtensionsBlocked); |
| } |
| |
| { |
| // Extensions button has "any extension has access" icon type when it's not |
| // an user restricted site and 1+ extensions have |
| // site access granted. Note that by default extensions have granted access. |
| extensions::PermissionsManagerWaiter manager_waiter(manager); |
| manager->RemoveUserRestrictedSite(url_origin); |
| manager_waiter.WaitForUserPermissionsSettingsChange(); |
| WaitForAnimation(); |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kAnyExtensionHasAccess); |
| } |
| |
| { |
| // Extension button has "default" icon type when it's not an user restricted |
| // site and no extensions have site access granted. |
| // To achieve this, we withhold host permissions in the only extension |
| // installed. |
| WithholdHostPermissions(extension.get()); |
| WaitForAnimation(); |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kDefault); |
| } |
| } |
| |
| // Test that the extension button state takes into account chrome restricted |
| // sites. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| ExtensionsButton_ChromeRestrictedSite) { |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| |
| const GURL restricted_url("chrome://extensions"); |
| NavigateAndCommit(restricted_url); |
| |
| // Extensions button has "all extensions blocked" icon type for chrome |
| // restricted sites. |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kAllExtensionsBlocked); |
| } |
| |
| // Tests that extensions appear in the request access button iff they have a |
| // site access request. |
| TEST_F(ExtensionsToolbarContainerUnitTest, RequestAccessButton_Extensions) { |
| auto extension_A = |
| InstallExtensionWithPermissions("Extension A", {"activeTab"}); |
| auto extension_B = |
| InstallExtensionWithHostPermissions("Extension B", {"*://www.b.com/*"}); |
| auto extension_C = |
| InstallExtensionWithHostPermissions("Extension C", {"<all_urls>"}); |
| WithholdHostPermissions(extension_B.get()); |
| WithholdHostPermissions(extension_C.get()); |
| |
| // Navigate to a site only explicitly requested by extension C. Verify |
| // request access button is not visible, since no extension has added a |
| // request. |
| NavigateAndCommit(GURL("http://www.other.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request for extension A and verify it's not visible on |
| // the request access button since extensions with only activeTab can't add a |
| // request. |
| AddSiteAccessRequest(*extension_A, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request for extension B and verify it's not visible on |
| // the request access button since extension didn't request access for the |
| // current site. |
| AddSiteAccessRequest(*extension_B, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request for extension C and verify it's visible on the |
| // request access button. |
| AddSiteAccessRequest(*extension_C, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_C->id())); |
| |
| // Navigate to a site only explicitly requested by extension B and C. Verify |
| // request access button is not visible, since requests are reset on |
| // cross-origin navigations. |
| NavigateAndCommit(GURL("http://www.b.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request for extension B and verify it's visible on |
| // the request access button. |
| AddSiteAccessRequest(*extension_B, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_B->id())); |
| |
| // Add a site access request for extension C and verify it's visible on the |
| // request access button. |
| AddSiteAccessRequest(*extension_C, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_B->id(), extension_C->id())); |
| |
| // Remove the site access request for extension B and verify only extension |
| // C is visible on the request access button. |
| RemoveSiteAccessRequest(*extension_B, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_C->id())); |
| |
| // Remove the site access request for extension C and verify request access |
| // button is not visible. |
| RemoveSiteAccessRequest(*extension_C, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| // Tests that an extension appears in the request access button iff it has a |
| // site access request that matches the given pattern filter. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_RequestWithPattern) { |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| // Navigate to a site and verify request access button is not visible, since |
| // no extension has added a request. |
| NavigateAndCommit(GURL("http://www.example.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request with filter that does not match the current web |
| // contents. Verify request access button is hidden. |
| URLPattern filter(extensions::Extension::kValidHostPermissionSchemes, |
| "http://www.other.com/"); |
| AddSiteAccessRequest( |
| *extension, browser()->tab_strip_model()->GetActiveWebContents(), filter); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add a site access request with filter that matches the current web |
| // contents. Verify extension is visible on the request access button. |
| filter = URLPattern(extensions::Extension::kValidHostPermissionSchemes, |
| "http://www.example.com/"); |
| AddSiteAccessRequest( |
| *extension, browser()->tab_strip_model()->GetActiveWebContents(), filter); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension->id())); |
| |
| // Add a site access request with filter that does not match the current web |
| // contents. Verify request access button is hidden (previous request was |
| // removed). |
| filter = URLPattern(extensions::Extension::kValidHostPermissionSchemes, |
| "http://www.other.com/"); |
| AddSiteAccessRequest( |
| *extension, browser()->tab_strip_model()->GetActiveWebContents(), filter); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| // Tests that an extension's site access request is removed when the extension |
| // is granted site access. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_ExtensionGrantedSiteAccess) { |
| auto extension_A = InstallExtensionWithHostPermissions( |
| "Extension A", {"*://www.example.com/*"}); |
| auto extension_B = |
| InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"}); |
| WithholdHostPermissions(extension_A.get()); |
| WithholdHostPermissions(extension_B.get()); |
| |
| // Navigate to a site and verify request access button is not visible, since |
| // no extension has added a request. |
| NavigateAndCommit(GURL("http://www.example.com")); |
| auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add site access requests for both extensions and verify they are visible |
| // on the request access button. |
| AddSiteAccessRequest(*extension_A, web_contents); |
| AddSiteAccessRequest(*extension_B, web_contents); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_A->id(), extension_B->id())); |
| |
| // Grant site access to extension B and verify request access button only has |
| // extension A, since extension B's request was removed once the extension |
| // gained access to the site. |
| UpdateUserSiteAccess(*extension_B, web_contents, |
| PermissionsManager::UserSiteAccess::kOnSite); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_A->id())); |
| } |
| |
| // Tests that requests are reset on cross-origin navigations. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButtonVisibility_NavigationBetweenPages) { |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| NavigateAndCommit(GURL("http://www.a.com")); |
| AddSiteAccessRequest(*extension, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension->id())); |
| |
| // Navigate to a same-origin site and verify request access button has |
| // extension. |
| NavigateAndCommit(GURL("http://www.a.com/path")); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension->id())); |
| |
| // Navigate to a cross-origin site and verify request access button is hidden. |
| NavigateAndCommit(GURL("http://www.b.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Navigate to the original site and verify request access button is hidden, |
| // since requests are reset on cross-origin navigations. |
| NavigateAndCommit(GURL("http://www.a.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| // Tests that the request access button is visible for matched patterns on |
| // same-origin navigations. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_NavigationBetweenPages_RequestWithPattern) { |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| // Navigate to a site and verify request access button is hidden, since |
| // no extension has added a request. |
| NavigateAndCommit(GURL("http://www.example.com")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Add site access request for extension with a filter that doesn't match the |
| // current web contents. Verify request access button is hidden. |
| URLPattern filter(extensions::Extension::kValidHostPermissionSchemes, |
| "*://*/path"); |
| AddSiteAccessRequest( |
| *extension, browser()->tab_strip_model()->GetActiveWebContents(), filter); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Navigate to a same-origin site that matches the filter. Verify extension is |
| // visible on the request access button. |
| NavigateAndCommit(GURL("http://www.example.com/path")); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension->id())); |
| |
| // Add site access request for extension with a filter that doesn't have the |
| // same origin as the current web contents. Verify request access button is |
| // hidden. |
| filter = URLPattern(extensions::Extension::kValidHostPermissionSchemes, |
| "http://www.other.com/path"); |
| AddSiteAccessRequest( |
| *extension, browser()->tab_strip_model()->GetActiveWebContents(), filter); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Navigate to a cross-origin site that matches the filters. Since it's a |
| // cross-origin navigation, requests are reset. Therefore, verify request |
| // access button is hidden. |
| NavigateAndCommit(GURL("http://www.other.com/path")); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| // Test that request access button is visible based on the user site setting |
| // selected. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_UserSiteSetting) { |
| const GURL url("http://www.url.com"); |
| auto url_origin = url::Origin::Create(url); |
| |
| // Install an extension and withhold permissions so request access button can |
| // be visible. |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| // Navigate to url and add a site request for the extension. |
| NavigateAndCommit(url); |
| AddSiteAccessRequest(*extension, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| |
| // A site has "customize by extensions" site setting by default, |
| ASSERT_EQ(GetUserSiteSetting(url), |
| PermissionsManager::UserSiteSetting::kCustomizeByExtension); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| |
| auto* manager = PermissionsManager::Get(profile()); |
| |
| { |
| // Request access button is not visible in restricted sites. |
| extensions::PermissionsManagerWaiter manager_waiter(manager); |
| manager->AddUserRestrictedSite(url_origin); |
| manager_waiter.WaitForUserPermissionsSettingsChange(); |
| WaitForAnimation(); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| { |
| // Request acesss button is visible if site is not restricted, |
| // and at least one extension has a site access request. |
| extensions::PermissionsManagerWaiter manager_waiter(manager); |
| manager->RemoveUserRestrictedSite(url_origin); |
| manager_waiter.WaitForUserPermissionsSettingsChange(); |
| WaitForAnimation(); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| } |
| } |
| |
| // Tests that an extension with a site access request but not allowed to show |
| // requests in the toolbar is not shown in the request access button. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_ExtensionsNotAllowedInButton) { |
| // Add two extensions that request access to all urls, and withhold their |
| // site access. |
| auto extension_a = |
| InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"}); |
| auto extension_b = |
| InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"}); |
| WithholdHostPermissions(extension_a.get()); |
| WithholdHostPermissions(extension_b.get()); |
| |
| // By default, both extensions are allowed to show requests in requests access |
| // button. However, request access button is not visible because we haven't |
| // navigated to a site yet (and extensions haven't added any site access |
| // requests). |
| SitePermissionsHelper permissions_helper(browser()->profile()); |
| EXPECT_TRUE( |
| permissions_helper.ShowAccessRequestsInToolbar(extension_a->id())); |
| EXPECT_TRUE( |
| permissions_helper.ShowAccessRequestsInToolbar(extension_b->id())); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Navigate to an url that both extensions want access to, and add site access |
| // requests for both. |
| const GURL url("http://www.example.com"); |
| NavigateAndCommit(url); |
| auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| AddSiteAccessRequest(*extension_a, web_contents); |
| AddSiteAccessRequest(*extension_b, web_contents); |
| |
| // Verify request access button has both extensions. |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_EQ( |
| request_access_button()->GetText(), |
| l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 2)); |
| |
| // Disallow extension A in the request access button. Verify only extension A |
| // is visible in the button. |
| permissions_helper.SetShowAccessRequestsInToolbar(extension_a->id(), false); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_EQ( |
| request_access_button()->GetText(), |
| l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1)); |
| |
| // Disallow extension B in the request access button. Verify button is not |
| // visible anymore. |
| permissions_helper.SetShowAccessRequestsInToolbar(extension_b->id(), false); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| // Test that an extension's request which is dismissed is not visible in the |
| // request access button. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_RequestDismissed) { |
| // Add two extensions that request access to all urls, and withhold their |
| // site access. |
| auto extension_a = |
| InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"}); |
| auto extension_b = |
| InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"}); |
| WithholdHostPermissions(extension_a.get()); |
| WithholdHostPermissions(extension_b.get()); |
| |
| // By default, both extensions are allowed to show requests in requests access |
| // button. However, request access button is not visible because we haven't |
| // navigated to a site yet (and extensions haven't added any site access |
| // requests). |
| SitePermissionsHelper permissions_helper(browser()->profile()); |
| EXPECT_TRUE( |
| permissions_helper.ShowAccessRequestsInToolbar(extension_a->id())); |
| EXPECT_TRUE( |
| permissions_helper.ShowAccessRequestsInToolbar(extension_b->id())); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| |
| // Navigate to an url that both extensions want access to, and add site access |
| // requests for both. |
| const GURL url("http://www.example.com"); |
| NavigateAndCommit(url); |
| auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| AddSiteAccessRequest(*extension_a, web_contents); |
| AddSiteAccessRequest(*extension_b, web_contents); |
| |
| // Verify request access button has both extensions. |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_EQ( |
| request_access_button()->GetText(), |
| l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 2)); |
| |
| int tab_id = extensions::ExtensionTabUtil::GetTabId(web_contents); |
| auto* permissions_manager = extensions::PermissionsManager::Get(profile()); |
| |
| // Dismiss extension A's requests. Verify only extension B is visible in the |
| // button. |
| permissions_manager->UserDismissedSiteAccessRequest(web_contents, tab_id, |
| extension_a->id()); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| EXPECT_EQ( |
| request_access_button()->GetText(), |
| l10n_util::GetStringFUTF16Int(IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON, 1)); |
| |
| // Dismiss extension B's requests. Verify button is not visible anymore. |
| permissions_manager->UserDismissedSiteAccessRequest(web_contents, tab_id, |
| extension_b->id()); |
| EXPECT_FALSE(IsRequestAccessButtonVisible()); |
| } |
| |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_OnPressedExecuteAction) { |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| // Navigate to url and add a site access request for extension. |
| const GURL url("http://www.example.com"); |
| NavigateAndCommit(url); |
| AddSiteAccessRequest(*extension, |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| LayoutContainerIfNecessary(); |
| |
| constexpr char kActivatedUserAction[] = |
| "Extensions.Toolbar.ExtensionsActivatedFromRequestAccessButton"; |
| base::UserActionTester user_action_tester; |
| auto* permissions = PermissionsManager::Get(profile()); |
| |
| // Request access button is visible because the extension is requesting |
| // access. |
| ASSERT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_EQ(user_action_tester.GetActionCount(kActivatedUserAction), 0); |
| EXPECT_EQ(permissions->GetUserSiteAccess(*extension, url), |
| PermissionsManager::UserSiteAccess::kOnClick); |
| |
| // Extension menu button has default state since extensions are not blocked, |
| // and there is no extension with access to the site. |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kDefault); |
| |
| ClickButton(request_access_button()); |
| WaitForAnimation(); |
| LayoutContainerIfNecessary(); |
| |
| // Verify extension was executed and extensions menu button has "any |
| // extension has access" state. Extension's site access should have not |
| // changed, since clicking the button grants one time access. |
| EXPECT_EQ(user_action_tester.GetActionCount(kActivatedUserAction), 1); |
| EXPECT_EQ(extensions_button()->state(), |
| ExtensionsToolbarButton::State::kAnyExtensionHasAccess); |
| EXPECT_EQ(permissions->GetUserSiteAccess(*extension, url), |
| PermissionsManager::UserSiteAccess::kOnClick); |
| |
| // Verify confirmation message appears on the request access button. |
| EXPECT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_EQ(request_access_button()->GetText(), |
| l10n_util::GetStringUTF16( |
| IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON_DISMISSED_TEXT)); |
| |
| // Force the confirmation to be collapsed. |
| task_environment()->AdvanceClock(kConfirmationDisplayDuration); |
| base::RunLoop().RunUntilIdle(); |
| WaitForAnimation(); |
| |
| // Verify the request access button is hidden. |
| ASSERT_FALSE(request_access_button()->GetVisible()); |
| } |
| |
| // Tests that if an update comes in between the request access button is clicked |
| // and the confirmation is collapsed, the button is updated afterwards with the |
| // correct information. |
| TEST_F(ExtensionsToolbarContainerUnitTest, |
| RequestAccessButton_UpdateInBetweenClickAndConfirmationCollapse) { |
| auto extension_A = |
| InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"}); |
| auto extension_B = |
| InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"}); |
| auto extension_C = |
| InstallExtensionWithHostPermissions("Extension C", {"<all_urls>"}); |
| WithholdHostPermissions(extension_A.get()); |
| WithholdHostPermissions(extension_B.get()); |
| WithholdHostPermissions(extension_C.get()); |
| |
| const GURL url("http://www.example.com"); |
| NavigateAndCommit(url); |
| LayoutContainerIfNecessary(); |
| |
| // Add site access requests for extension A and B. |
| auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| AddSiteAccessRequest(*extension_A, web_contents); |
| AddSiteAccessRequest(*extension_B, web_contents); |
| LayoutContainerIfNecessary(); |
| |
| // Request access button is visible because extension A and B have site access |
| // requests. |
| EXPECT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_A->id(), extension_B->id())); |
| |
| ClickButton(request_access_button()); |
| WaitForAnimation(); |
| LayoutContainerIfNecessary(); |
| |
| // Verify confirmation message appears on the request access button after |
| // clicking on it |
| EXPECT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_EQ(request_access_button()->GetText(), |
| l10n_util::GetStringUTF16( |
| IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON_DISMISSED_TEXT)); |
| |
| // Add a site access request for extension C before the confirmation is |
| // collapsed. |
| AddSiteAccessRequest(*extension_C, web_contents); |
| |
| // Confirmation is still showing since collapse time hasn't elapsed. |
| EXPECT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_EQ(request_access_button()->GetText(), |
| l10n_util::GetStringUTF16( |
| IDS_EXTENSIONS_REQUEST_ACCESS_BUTTON_DISMISSED_TEXT)); |
| |
| // Force the confirmation to be collapsed. |
| task_environment()->AdvanceClock(kConfirmationDisplayDuration); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Verify the request access button is visible since extension C is now |
| // requesting access. |
| EXPECT_TRUE(request_access_button()->GetVisible()); |
| EXPECT_THAT(request_access_button()->GetExtensionIdsForTesting(), |
| testing::ElementsAre(extension_C->id())); |
| } |
| |
| class ExtensionsToolbarContainerWithPermittedSitesUnitTest |
| : public ExtensionsToolbarContainerUnitTest { |
| public: |
| ExtensionsToolbarContainerWithPermittedSitesUnitTest() { |
| std::vector<base::test::FeatureRef> enabled_features = { |
| extensions_features::kExtensionsMenuAccessControl, |
| extensions_features::kExtensionsMenuAccessControlWithPermittedSites}; |
| std::vector<base::test::FeatureRef> disabled_features; |
| feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| ExtensionsToolbarContainerWithPermittedSitesUnitTest( |
| const ExtensionsToolbarContainerWithPermittedSitesUnitTest&) = delete; |
| const ExtensionsToolbarContainerWithPermittedSitesUnitTest& operator=( |
| const ExtensionsToolbarContainerWithPermittedSitesUnitTest&) = delete; |
| ~ExtensionsToolbarContainerWithPermittedSitesUnitTest() override = default; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Test that request access button is visible based on the user site setting |
| // selected. |
| TEST_F(ExtensionsToolbarContainerWithPermittedSitesUnitTest, |
| RequestAccessButtonVisibilityOnPermittedSites) { |
| const GURL url("http://www.url.com"); |
| auto url_origin = url::Origin::Create(url); |
| |
| // Install an extension and withhold permissions so request access button can |
| // be visible. |
| auto extension = |
| InstallExtensionWithHostPermissions("Extension", {"<all_urls>"}); |
| WithholdHostPermissions(extension.get()); |
| |
| // Navigate to a site and add a site access request for the extension. |
| NavigateAndCommit(url); |
| auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| AddSiteAccessRequest(*extension, web_contents); |
| |
| // A site has "customize by extensions" site setting by default, |
| ASSERT_EQ(GetUserSiteSetting(url), |
| PermissionsManager::UserSiteSetting::kCustomizeByExtension); |
| EXPECT_TRUE(IsRequestAccessButtonVisible()); |
| |
| // Request access button is not visible in permitted sites. |
| auto* manager = PermissionsManager::Get(profile()); |
| extensions::PermissionsManagerWaiter waiter(manager); |
| manager->AddUserPermittedSite(url_origin); |
| waiter.WaitForUserPermissionsSettingsChange(); |
| WaitForAnimation(); |
| |
| // Request access button visibility is the same for other site settings, which |
| // is already tested, regardless of whether permitted sites are supported or |
| // not. |
| } |