| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/command_line.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/macros.h" | 
 | #include "base/values.h" | 
 | #include "build/chromeos_buildflags.h" | 
 | #include "chrome/browser/chrome_notification_types.h" | 
 | #include "chrome/browser/extensions/active_tab_permission_granter.h" | 
 | #include "chrome/browser/extensions/chrome_test_extension_loader.h" | 
 | #include "chrome/browser/extensions/extension_service.h" | 
 | #include "chrome/browser/extensions/extension_service_test_base.h" | 
 | #include "chrome/browser/extensions/extension_util.h" | 
 | #include "chrome/browser/extensions/tab_helper.h" | 
 | #include "chrome/browser/extensions/test_extension_system.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/common/webui_url_constants.h" | 
 | #include "chrome/test/base/chrome_render_view_host_test_harness.h" | 
 | #include "chrome/test/base/testing_profile.h" | 
 | #include "components/sessions/content/session_tab_helper.h" | 
 | #include "components/version_info/version_info.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/navigation_details.h" | 
 | #include "content/public/browser/navigation_entry.h" | 
 | #include "content/public/browser/notification_service.h" | 
 | #include "content/public/browser/notification_types.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/test/navigation_simulator.h" | 
 | #include "content/public/test/web_contents_tester.h" | 
 | #include "extensions/browser/disable_reason.h" | 
 | #include "extensions/browser/extension_registry.h" | 
 | #include "extensions/browser/test_extension_registry_observer.h" | 
 | #include "extensions/common/constants.h" | 
 | #include "extensions/common/extension.h" | 
 | #include "extensions/common/extension_builder.h" | 
 | #include "extensions/common/extensions_client.h" | 
 | #include "extensions/common/features/feature.h" | 
 | #include "extensions/common/features/feature_channel.h" | 
 | #include "extensions/common/permissions/permissions_data.h" | 
 | #include "extensions/common/value_builder.h" | 
 | #include "extensions/test/test_extension_dir.h" | 
 |  | 
 | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
 | #include "ash/constants/ash_switches.h" | 
 | #include "base/run_loop.h" | 
 | #include "chrome/browser/ash/app_mode/kiosk_app_manager.h" | 
 | #include "chrome/browser/ash/login/users/chrome_user_manager_impl.h" | 
 | #include "chrome/browser/ash/profiles/profile_helper.h" | 
 | #include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h" | 
 | #include "chrome/browser/ui/ash/test_wallpaper_controller.h" | 
 | #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h" | 
 | #include "chrome/test/base/scoped_testing_local_state.h" | 
 | #include "chrome/test/base/testing_browser_process.h" | 
 | #include "chromeos/login/login_state/scoped_test_public_session_login_state.h" | 
 | #include "components/account_id/account_id.h" | 
 | #include "components/sync/driver/sync_driver_switches.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "extensions/browser/extension_dialog_auto_confirm.h" | 
 | #endif | 
 |  | 
 | using base::DictionaryValue; | 
 | using base::ListValue; | 
 | using content::BrowserThread; | 
 | using content::NavigationController; | 
 | using extensions::mojom::APIPermissionID; | 
 |  | 
 | namespace extensions { | 
 | namespace { | 
 |  | 
 | scoped_refptr<const Extension> CreateTestExtension( | 
 |     const std::string& name, | 
 |     bool has_active_tab_permission, | 
 |     bool has_tab_capture_permission) { | 
 |   ExtensionBuilder builder(name); | 
 |   if (has_active_tab_permission) | 
 |     builder.AddPermission("activeTab"); | 
 |   if (has_tab_capture_permission) | 
 |     builder.AddPermission("tabCapture"); | 
 |  | 
 |   return builder.Build(); | 
 | } | 
 |  | 
 | enum PermittedFeature { | 
 |   PERMITTED_NONE, | 
 |   PERMITTED_SCRIPT_ONLY, | 
 |   PERMITTED_CAPTURE_ONLY, | 
 |   PERMITTED_BOTH | 
 | }; | 
 |  | 
 | class ActiveTabPermissionGranterTestDelegate | 
 |     : public ActiveTabPermissionGranter::Delegate { | 
 |  public: | 
 |   ActiveTabPermissionGranterTestDelegate() {} | 
 |   ~ActiveTabPermissionGranterTestDelegate() override {} | 
 |  | 
 |   // ActiveTabPermissionGranterTestDelegate::Delegate | 
 |   bool ShouldGrantActiveTabOrPrompt(const Extension* extension, | 
 |                                     content::WebContents* contents) override { | 
 |     should_grant_call_count_++; | 
 |     return should_grant_; | 
 |   } | 
 |  | 
 |   void SetShouldGrant(bool should_grant) { | 
 |     should_grant_ = should_grant; | 
 |   } | 
 |  | 
 |   int should_grant_call_count() { return should_grant_call_count_; } | 
 |  | 
 |  private: | 
 |   bool should_grant_ = false; | 
 |   int should_grant_call_count_ = 0; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ActiveTabPermissionGranterTestDelegate); | 
 | }; | 
 |  | 
 | class ActiveTabTest : public ChromeRenderViewHostTestHarness { | 
 |  protected: | 
 |   ActiveTabTest() | 
 |       : current_channel(version_info::Channel::DEV), | 
 |         extension(CreateTestExtension("deadbeef", true, false)), | 
 |         another_extension(CreateTestExtension("feedbeef", true, false)), | 
 |         extension_without_active_tab(CreateTestExtension("badbeef", | 
 |                                                          false, | 
 |                                                          false)), | 
 |         extension_with_tab_capture(CreateTestExtension("cafebeef", | 
 |                                                        true, | 
 |                                                        true)) {} | 
 |  | 
 |   void SetUp() override { | 
 |     ChromeRenderViewHostTestHarness::SetUp(); | 
 |     TabHelper::CreateForWebContents(web_contents()); | 
 |  | 
 |     // We need to add extensions to the ExtensionService; else trying to commit | 
 |     // any of their URLs fails and redirects to about:blank. | 
 |     ExtensionService* service = | 
 |         static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())) | 
 |             ->CreateExtensionService(base::CommandLine::ForCurrentProcess(), | 
 |                                      base::FilePath(), false); | 
 |     service->AddExtension(extension.get()); | 
 |     service->AddExtension(another_extension.get()); | 
 |     service->AddExtension(extension_without_active_tab.get()); | 
 |     service->AddExtension(extension_with_tab_capture.get()); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
 |     ash::KioskAppManager::Shutdown(); | 
 | #endif | 
 |     ChromeRenderViewHostTestHarness::TearDown(); | 
 |   } | 
 |  | 
 |   int tab_id() { | 
 |     return sessions::SessionTabHelper::IdForTab(web_contents()).id(); | 
 |   } | 
 |  | 
 |   ActiveTabPermissionGranter* active_tab_permission_granter() { | 
 |     return extensions::TabHelper::FromWebContents(web_contents())-> | 
 |         active_tab_permission_granter(); | 
 |   } | 
 |  | 
 |   bool IsAllowed(const scoped_refptr<const Extension>& extension, | 
 |                  const GURL& url) { | 
 |     return IsAllowed(extension, url, PERMITTED_BOTH, tab_id()); | 
 |   } | 
 |  | 
 |   bool IsAllowed(const scoped_refptr<const Extension>& extension, | 
 |                  const GURL& url, | 
 |                  PermittedFeature feature) { | 
 |     return IsAllowed(extension, url, feature, tab_id()); | 
 |   } | 
 |  | 
 |   bool IsAllowed(const scoped_refptr<const Extension>& extension, | 
 |                  const GURL& url, | 
 |                  PermittedFeature feature, | 
 |                  int tab_id) { | 
 |     const PermissionsData* permissions_data = extension->permissions_data(); | 
 |     bool script = | 
 |         permissions_data->CanAccessPage(url, tab_id, nullptr) && | 
 |         permissions_data->CanRunContentScriptOnPage(url, tab_id, nullptr); | 
 |     bool capture = permissions_data->CanCaptureVisiblePage( | 
 |         url, tab_id, NULL, extensions::CaptureRequirement::kActiveTabOrAllUrls); | 
 |     switch (feature) { | 
 |       case PERMITTED_SCRIPT_ONLY: | 
 |         return script && !capture; | 
 |       case PERMITTED_CAPTURE_ONLY: | 
 |         return capture && !script; | 
 |       case PERMITTED_BOTH: | 
 |         return script && capture; | 
 |       case PERMITTED_NONE: | 
 |         return !script && !capture; | 
 |     } | 
 |     NOTREACHED(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool IsBlocked(const scoped_refptr<const Extension>& extension, | 
 |                  const GURL& url) { | 
 |     return IsBlocked(extension, url, tab_id()); | 
 |   } | 
 |  | 
 |   bool IsBlocked(const scoped_refptr<const Extension>& extension, | 
 |                  const GURL& url, | 
 |                  int tab_id) { | 
 |     return IsAllowed(extension, url, PERMITTED_NONE, tab_id); | 
 |   } | 
 |  | 
 |   bool HasTabsPermission(const scoped_refptr<const Extension>& extension) { | 
 |     return HasTabsPermission(extension, tab_id()); | 
 |   } | 
 |  | 
 |   bool HasTabsPermission(const scoped_refptr<const Extension>& extension, | 
 |                          int tab_id) { | 
 |     return extension->permissions_data()->HasAPIPermissionForTab( | 
 |         tab_id, APIPermissionID::kTab); | 
 |   } | 
 |  | 
 |   bool IsGrantedForTab(const Extension* extension, | 
 |                        const content::WebContents* web_contents) { | 
 |     return extension->permissions_data()->HasAPIPermissionForTab( | 
 |         sessions::SessionTabHelper::IdForTab(web_contents).id(), | 
 |         APIPermissionID::kTab); | 
 |   } | 
 |  | 
 |   // TODO(justinlin): Remove when tabCapture is moved to stable. | 
 |   ScopedCurrentChannel current_channel; | 
 |  | 
 |   // An extension with the activeTab permission. | 
 |   scoped_refptr<const Extension> extension; | 
 |  | 
 |   // Another extension with activeTab (for good measure). | 
 |   scoped_refptr<const Extension> another_extension; | 
 |  | 
 |   // An extension without the activeTab permission. | 
 |   scoped_refptr<const Extension> extension_without_active_tab; | 
 |  | 
 |   // An extension with both the activeTab and tabCapture permission. | 
 |   scoped_refptr<const Extension> extension_with_tab_capture; | 
 | }; | 
 |  | 
 | TEST_F(ActiveTabTest, GrantToSinglePage) { | 
 |   GURL google("http://www.google.com"); | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   // No access unless it's been granted. | 
 |   EXPECT_TRUE(IsBlocked(extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   EXPECT_FALSE(HasTabsPermission(extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(another_extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(extension_without_active_tab)); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_without_active_tab.get()); | 
 |  | 
 |   // Granted to extension and extension_without_active_tab, but the latter | 
 |   // doesn't have the activeTab permission so not granted. | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   // Other subdomains shouldn't be given access. | 
 |   GURL mail_google("http://mail.google.com"); | 
 |   EXPECT_TRUE(IsBlocked(extension, mail_google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, mail_google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, mail_google)); | 
 |  | 
 |   // Reloading the page should not clear the active permissions, since the | 
 |   // user remains on the same site. | 
 |   content::NavigationSimulator::Reload(web_contents()); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   EXPECT_TRUE(HasTabsPermission(extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(another_extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(extension_without_active_tab)); | 
 |  | 
 |   // And grant a few more times redundantly for good measure. | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |   EXPECT_TRUE(IsAllowed(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   // Navigating to a new URL should clear the active permissions. | 
 |   GURL chromium("http://www.chromium.org"); | 
 |   NavigateAndCommit(chromium); | 
 |  | 
 |   EXPECT_TRUE(IsBlocked(extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   EXPECT_TRUE(IsBlocked(extension, chromium)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, chromium)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium)); | 
 |  | 
 |   EXPECT_FALSE(HasTabsPermission(extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(another_extension)); | 
 |   EXPECT_FALSE(HasTabsPermission(extension_without_active_tab)); | 
 |  | 
 |   // Should be able to grant to multiple extensions at the same time (if they | 
 |   // have the activeTab permission, of course). | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_without_active_tab.get()); | 
 |  | 
 |   EXPECT_TRUE(IsBlocked(extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium)); | 
 |   EXPECT_TRUE(IsAllowed(another_extension, chromium)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium)); | 
 |  | 
 |   // Should be able to go back to URLs that were previously cleared. | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_without_active_tab.get()); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |   EXPECT_TRUE(IsAllowed(another_extension, google)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, google)); | 
 |  | 
 |   EXPECT_TRUE(IsBlocked(extension, chromium)); | 
 |   EXPECT_TRUE(IsBlocked(another_extension, chromium)); | 
 |   EXPECT_TRUE(IsBlocked(extension_without_active_tab, chromium)); | 
 | } | 
 |  | 
 | TEST_F(ActiveTabTest, CapturingPagesWithActiveTab) { | 
 |   std::vector<GURL> test_urls = { | 
 |       GURL("https://example.com"), | 
 |       GURL(chrome::kChromeUIVersionURL), | 
 |       GURL(chrome::kChromeUINewTabURL), | 
 |       GURL("http://[2607:f8b0:4005:805::200e]"), | 
 |       ExtensionsClient::Get()->GetWebstoreBaseURL(), | 
 |       extension->GetResourceURL("test.html"), | 
 |       another_extension->GetResourceURL("test.html"), | 
 |   }; | 
 |  | 
 |   const GURL kAboutBlank("about:blank"); | 
 |  | 
 |   for (const GURL& url : test_urls) { | 
 |     SCOPED_TRACE(url); | 
 |     NavigateAndCommit(url); | 
 |     EXPECT_EQ(url, web_contents()->GetLastCommittedURL()); | 
 |     // By default, there should be no access. | 
 |     EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |         url, tab_id(), nullptr /*error*/, | 
 |         extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |     // Granting permission should allow page capture. | 
 |     active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |     EXPECT_TRUE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |         url, tab_id(), nullptr /*error*/, | 
 |         extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |     // Navigating away should revoke access. | 
 |     NavigateAndCommit(kAboutBlank); | 
 |     EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |         url, tab_id(), nullptr /*error*/, | 
 |         extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(ActiveTabTest, Unloading) { | 
 |   // Some semi-arbitrary setup. | 
 |   GURL google("http://www.google.com"); | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |  | 
 |   EXPECT_TRUE(IsGrantedForTab(extension.get(), web_contents())); | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |  | 
 |   // Unloading the extension should clear its tab permissions. | 
 |   ExtensionSystem::Get(web_contents()->GetBrowserContext()) | 
 |       ->extension_service() | 
 |       ->DisableExtension(extension->id(), disable_reason::DISABLE_USER_ACTION); | 
 |  | 
 |   // Note: can't EXPECT_FALSE(IsAllowed) here because uninstalled extensions | 
 |   // are just that... considered to be uninstalled, and the manager might | 
 |   // just ignore them from here on. | 
 |  | 
 |   // Granting the extension again should give them back. | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |  | 
 |   EXPECT_TRUE(IsGrantedForTab(extension.get(), web_contents())); | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 | } | 
 |  | 
 | TEST_F(ActiveTabTest, OnlyActiveTab) { | 
 |   GURL google("http://www.google.com"); | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, google, PERMITTED_BOTH, tab_id())); | 
 |   EXPECT_TRUE(IsBlocked(extension, google, tab_id() + 1)); | 
 |   EXPECT_FALSE(HasTabsPermission(extension, tab_id() + 1)); | 
 | } | 
 |  | 
 | TEST_F(ActiveTabTest, SameDocumentNavigations) { | 
 |   GURL google("http://www.google.com"); | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |  | 
 |   // Perform a same-document navigation. The extension should not lose the | 
 |   // temporary permission. | 
 |   GURL google_h1("http://www.google.com#h1"); | 
 |   NavigateAndCommit(google_h1); | 
 |  | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 |   EXPECT_TRUE(IsAllowed(extension, google_h1)); | 
 |  | 
 |   GURL chromium("http://www.chromium.org"); | 
 |   NavigateAndCommit(chromium); | 
 |  | 
 |   EXPECT_FALSE(IsAllowed(extension, google)); | 
 |   EXPECT_FALSE(IsAllowed(extension, google_h1)); | 
 |   EXPECT_FALSE(IsAllowed(extension, chromium)); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |  | 
 |   EXPECT_FALSE(IsAllowed(extension, google)); | 
 |   EXPECT_FALSE(IsAllowed(extension, google_h1)); | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium)); | 
 |  | 
 |   GURL chromium_h1("http://www.chromium.org#h1"); | 
 |   NavigateAndCommit(chromium_h1); | 
 |  | 
 |   EXPECT_FALSE(IsAllowed(extension, google)); | 
 |   EXPECT_FALSE(IsAllowed(extension, google_h1)); | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium)); | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium_h1)); | 
 |  | 
 |   content::NavigationSimulator::Reload(web_contents()); | 
 |  | 
 |   EXPECT_FALSE(IsAllowed(extension, google)); | 
 |   EXPECT_FALSE(IsAllowed(extension, google_h1)); | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium)); | 
 |   EXPECT_TRUE(IsAllowed(extension, chromium_h1)); | 
 | } | 
 |  | 
 | TEST_F(ActiveTabTest, ChromeUrlGrants) { | 
 |   GURL internal(chrome::kChromeUIVersionURL); | 
 |   NavigateAndCommit(internal); | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_with_tab_capture.get()); | 
 |   // Do not grant tabs/hosts permissions for tab. | 
 |   EXPECT_TRUE(IsAllowed(extension_with_tab_capture, internal, | 
 |                         PERMITTED_CAPTURE_ONLY)); | 
 |   const PermissionsData* permissions_data = | 
 |       extension_with_tab_capture->permissions_data(); | 
 |   EXPECT_TRUE(permissions_data->HasAPIPermissionForTab( | 
 |       tab_id(), APIPermissionID::kTabCaptureForTab)); | 
 |  | 
 |   EXPECT_TRUE(IsBlocked(extension_with_tab_capture, internal, tab_id() + 1)); | 
 |   EXPECT_FALSE(permissions_data->HasAPIPermissionForTab( | 
 |       tab_id() + 1, APIPermissionID::kTabCaptureForTab)); | 
 | } | 
 |  | 
 | class ActiveTabDelegateTest : public ActiveTabTest { | 
 |  protected: | 
 |   ActiveTabDelegateTest() { | 
 |     auto delegate = std::make_unique<ActiveTabPermissionGranterTestDelegate>(); | 
 |     test_delegate_ = delegate.get(); | 
 |     ActiveTabPermissionGranter::SetPlatformDelegate(std::move(delegate)); | 
 |   } | 
 |  | 
 |   ~ActiveTabDelegateTest() override { | 
 |     ActiveTabPermissionGranter::SetPlatformDelegate(nullptr); | 
 |   } | 
 |  | 
 |   ActiveTabPermissionGranterTestDelegate* test_delegate_; | 
 | }; | 
 |  | 
 | // Test that the custom platform delegate works as expected. | 
 | TEST_F(ActiveTabDelegateTest, Delegate) { | 
 |   GURL google("http://www.google.com"); | 
 |   NavigateAndCommit(google); | 
 |  | 
 |   // Not granted because the delegate denies grant. | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   EXPECT_TRUE(IsBlocked(extension, google)); | 
 |  | 
 |   // This time it's granted because the delegate allows it. | 
 |   test_delegate_->SetShouldGrant(true); | 
 |   active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |   EXPECT_TRUE(IsAllowed(extension, google)); | 
 | } | 
 |  | 
 | // Regression test for crbug.com/833188. | 
 | TEST_F(ActiveTabDelegateTest, DelegateUsedOnlyWhenNeeded) { | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_without_active_tab.get()); | 
 |  | 
 |   EXPECT_EQ(0, test_delegate_->should_grant_call_count()); | 
 | } | 
 |  | 
 | #if BUILDFLAG(IS_CHROMEOS_ASH) | 
 | class ActiveTabManagedSessionTest : public ActiveTabTest { | 
 |  protected: | 
 |   ActiveTabManagedSessionTest() {} | 
 |  | 
 |   void SetUp() override { | 
 |     ActiveTabTest::SetUp(); | 
 |  | 
 |     // Necessary to prevent instantiation of ProfileSyncService, which messes | 
 |     // with our signin state below. | 
 |     base::CommandLine::ForCurrentProcess()->AppendSwitch( | 
 |         switches::kDisableSync); | 
 |     // Necessary because no ProfileManager instance exists in this test. | 
 |     base::CommandLine::ForCurrentProcess()->AppendSwitch( | 
 |         chromeos::switches::kIgnoreUserProfileMappingForTests); | 
 |     // Necessary to skip cryptohome/profile sanity check in | 
 |     // ChromeUserManagerImpl for fake user login. | 
 |     base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTestType); | 
 |  | 
 |     // Setup, login a public account user. | 
 |     const std::string user_id = "public@account.user"; | 
 |     const std::string user_email = user_id; | 
 |     const AccountId account_id = | 
 |         AccountId::FromUserEmailGaiaId(user_email, user_id); | 
 |     const std::string user_id_hash = | 
 |         chromeos::ProfileHelper::Get()->GetUserIdHashByUserIdForTesting( | 
 |             user_id); | 
 |  | 
 |     local_state_ = std::make_unique<ScopedTestingLocalState>( | 
 |         TestingBrowserProcess::GetGlobal()); | 
 |     wallpaper_controller_client_ = | 
 |         std::make_unique<WallpaperControllerClientImpl>(); | 
 |     wallpaper_controller_client_->InitForTesting(&test_wallpaper_controller_); | 
 |     g_browser_process->local_state()->SetString( | 
 |         "PublicAccountPendingDataRemoval", user_email); | 
 |     user_manager::UserManager::Get()->UserLoggedIn(account_id, user_id_hash, | 
 |                                                    true /* browser_restart */, | 
 |                                                    false /* is_child */); | 
 |     // Finish initialization - some things are run as separate tasks. | 
 |     base::RunLoop().RunUntilIdle(); | 
 |  | 
 |     google_ = GURL("http://www.google.com"); | 
 |     NavigateAndCommit(google_); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     // This one needs to be destructed here so it deregisters itself from | 
 |     // CrosSettings before that is destructed down the line inside | 
 |     // ChromeRenderViewHostTestHarness::TearDown. | 
 |     wallpaper_controller_client_.reset(); | 
 |  | 
 |     ash::ChromeUserManagerImpl::ResetPublicAccountDelegatesForTesting(); | 
 |     ash::ChromeUserManager::Get()->Shutdown(); | 
 |  | 
 |     ActiveTabTest::TearDown(); | 
 |   } | 
 |  | 
 |   std::unique_ptr<ScopedTestingLocalState> local_state_; | 
 |   TestWallpaperController test_wallpaper_controller_; | 
 |   std::unique_ptr<WallpaperControllerClientImpl> wallpaper_controller_client_; | 
 |   GURL google_; | 
 | }; | 
 |  | 
 | // Test that there's no permission prompt in Managed Sessions (Public Sessions | 
 | // v2) for activeTab. | 
 | TEST_F(ActiveTabManagedSessionTest, NoPromptInManagedSession) { | 
 |   chromeos::ScopedTestPublicSessionLoginState login_state( | 
 |       chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED); | 
 |  | 
 |   active_tab_permission_granter()->GrantIfRequested( | 
 |       extension_with_tab_capture.get()); | 
 |   EXPECT_TRUE(IsAllowed(extension_with_tab_capture, google_)); | 
 | } | 
 |  | 
 | // Keep the unique_ptr around until callback has been run and don't forget to | 
 | // unset the ActiveTabPermissionGranterDelegateChromeOS. | 
 | std::unique_ptr<permission_helper::RequestResolvedCallback> | 
 | QuitRunLoopOnRequestResolved(base::RunLoop* run_loop) { | 
 |   auto callback = std::make_unique<permission_helper::RequestResolvedCallback>( | 
 |       base::BindOnce([](base::RunLoop* run_loop, | 
 |                         const PermissionIDSet&) { run_loop->Quit(); }, | 
 |                      run_loop)); | 
 |   ActiveTabPermissionGranterDelegateChromeOS:: | 
 |       SetRequestResolvedCallbackForTesting(callback.get()); | 
 |   return callback; | 
 | } | 
 |  | 
 | // Test that the platform delegate is being set and the activeTab permission is | 
 | // prompted for in Public Sessions. | 
 | TEST_F(ActiveTabManagedSessionTest, | 
 |        DelegateIsSetAndPromptIsShownInPublicSession) { | 
 |   chromeos::ScopedTestPublicSessionLoginState login_state( | 
 |       chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT); | 
 |   // Grant and verify. | 
 |   { | 
 |     ScopedTestDialogAutoConfirm auto_confirm( | 
 |         ScopedTestDialogAutoConfirm::ACCEPT); | 
 |  | 
 |     // RunLoop needed to resolve the permission dialog. | 
 |     base::RunLoop run_loop; | 
 |     auto cb = QuitRunLoopOnRequestResolved(&run_loop); | 
 |     active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |     run_loop.Run(); | 
 |     EXPECT_TRUE(IsBlocked(extension, google_)); | 
 |  | 
 |     active_tab_permission_granter()->GrantIfRequested(extension.get()); | 
 |     EXPECT_TRUE(IsAllowed(extension, google_)); | 
 |   } | 
 |  | 
 |   // Deny and verify. Use a different extension so it doesn't trigger the | 
 |   // cache. | 
 |   { | 
 |     ScopedTestDialogAutoConfirm auto_confirm( | 
 |         ScopedTestDialogAutoConfirm::CANCEL); | 
 |  | 
 |     base::RunLoop run_loop; | 
 |     auto cb = QuitRunLoopOnRequestResolved(&run_loop); | 
 |     active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |     run_loop.Run(); | 
 |     EXPECT_TRUE(IsBlocked(another_extension, google_)); | 
 |  | 
 |     active_tab_permission_granter()->GrantIfRequested(another_extension.get()); | 
 |     EXPECT_TRUE(IsBlocked(another_extension, google_)); | 
 |   } | 
 |  | 
 |   // Cleanup. | 
 |   ActiveTabPermissionGranterDelegateChromeOS:: | 
 |       SetRequestResolvedCallbackForTesting(nullptr); | 
 | } | 
 | #endif  // BUILDFLAG(IS_CHROMEOS_ASH) | 
 |  | 
 | // An active tab test that includes an ExtensionService. | 
 | class ActiveTabWithServiceTest : public ExtensionServiceTestBase { | 
 |  public: | 
 |   ActiveTabWithServiceTest() {} | 
 |  | 
 |   void SetUp() override; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(ActiveTabWithServiceTest); | 
 | }; | 
 |  | 
 | void ActiveTabWithServiceTest::SetUp() { | 
 |   ExtensionServiceTestBase::SetUp(); | 
 | } | 
 |  | 
 | // Tests that an extension can only capture file:// URLs with the active tab | 
 | // permission when it has file access granted. | 
 | // Regression test for https://crbug.com/810220. | 
 | TEST_F(ActiveTabWithServiceTest, FileURLs) { | 
 |   InitializeEmptyExtensionService(); | 
 |  | 
 |   TestExtensionDir test_dir; | 
 |   test_dir.WriteManifest(R"( | 
 |     { | 
 |       "name": "Active Tab Capture With File Urls", | 
 |       "description": "Testing activeTab on file urls", | 
 |       "version": "0.1", | 
 |       "manifest_version": 2, | 
 |       "permissions": ["activeTab"] | 
 |     })"); | 
 |  | 
 |   ChromeTestExtensionLoader loader(profile()); | 
 |   loader.set_allow_file_access(false); | 
 |   scoped_refptr<const Extension> extension = | 
 |       loader.LoadExtension(test_dir.UnpackedPath()); | 
 |   ASSERT_TRUE(extension); | 
 |   const std::string id = extension->id(); | 
 |   ASSERT_TRUE(registry()->enabled_extensions().Contains(id)); | 
 |  | 
 |   EXPECT_FALSE(util::AllowFileAccess(id, profile())); | 
 |  | 
 |   std::unique_ptr<content::WebContents> web_contents( | 
 |       content::WebContentsTester::CreateTestWebContents(profile(), nullptr)); | 
 |   ASSERT_TRUE(web_contents); | 
 |  | 
 |   const GURL file_url("file:///foo"); | 
 |   ASSERT_TRUE(content::WebContentsTester::For(web_contents.get())); | 
 |   content::WebContentsTester::For(web_contents.get()) | 
 |       ->NavigateAndCommit(file_url); | 
 |   EXPECT_EQ(file_url, web_contents->GetLastCommittedURL()); | 
 |  | 
 |   TabHelper::CreateForWebContents(web_contents.get()); | 
 |   ActiveTabPermissionGranter* permission_granter = | 
 |       TabHelper::FromWebContents(web_contents.get()) | 
 |           ->active_tab_permission_granter(); | 
 |   ASSERT_TRUE(permission_granter); | 
 |   const int tab_id = | 
 |       sessions::SessionTabHelper::IdForTab(web_contents.get()).id(); | 
 |   EXPECT_NE(extension_misc::kUnknownTabId, tab_id); | 
 |  | 
 |   EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |       web_contents->GetLastCommittedURL(), tab_id, nullptr, | 
 |       extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |  | 
 |   permission_granter->GrantIfRequested(extension.get()); | 
 |   EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |       web_contents->GetLastCommittedURL(), tab_id, nullptr, | 
 |       extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |  | 
 |   permission_granter->RevokeForTesting(); | 
 |   TestExtensionRegistryObserver observer(registry(), id); | 
 |   // This will reload the extension, so we need to reset the extension pointer. | 
 |   util::SetAllowFileAccess(id, profile(), true); | 
 |   extension = observer.WaitForExtensionLoaded(); | 
 |   ASSERT_TRUE(extension); | 
 |  | 
 |   EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |       web_contents->GetLastCommittedURL(), tab_id, nullptr, | 
 |       extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 |   permission_granter->GrantIfRequested(extension.get()); | 
 |   EXPECT_TRUE(extension->permissions_data()->CanCaptureVisiblePage( | 
 |       web_contents->GetLastCommittedURL(), tab_id, nullptr, | 
 |       extensions::CaptureRequirement::kActiveTabOrAllUrls)); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace extensions |