blob: 2b4be91963f241a5014e7c75d8565b3953c2a15f [file] [log] [blame]
// 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/compiler_specific.h"
#include "base/macros.h"
#include "base/values.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/browser/sessions/session_tab_helper.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.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/common/frame_navigate_params.h"
#include "content/public/test/browser_side_navigation_test_utils.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_browser_thread.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 defined(OS_CHROMEOS)
#include "base/run_loop.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/extensions/active_tab_permission_granter_delegate_chromeos.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager_impl.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/ash/test_wallpaper_controller.h"
#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/login/scoped_test_public_session_login_state.h"
#include "components/account_id/account_id.h"
#include "components/browser_sync/browser_sync_switches.h"
#include "extensions/browser/extension_dialog_auto_confirm.h"
#endif
using base::DictionaryValue;
using base::ListValue;
using content::BrowserThread;
using content::NavigationController;
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 defined(OS_CHROMEOS)
chromeos::KioskAppManager::Shutdown();
#endif
ChromeRenderViewHostTestHarness::TearDown();
}
int tab_id() { return 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);
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, APIPermission::kTab);
}
bool IsGrantedForTab(const Extension* extension,
const content::WebContents* web_contents) {
return extension->permissions_data()->HasAPIPermissionForTab(
SessionTabHelper::IdForTab(web_contents).id(), APIPermission::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://version"),
GURL("chrome://newtab"),
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*/));
// Granting permission should allow page capture.
active_tab_permission_granter()->GrantIfRequested(extension.get());
EXPECT_TRUE(extension->permissions_data()->CanCaptureVisiblePage(
url, tab_id(), nullptr /*error*/));
// Navigating away should revoke access.
NavigateAndCommit(kAboutBlank);
EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage(
url, tab_id(), nullptr /*error*/));
}
}
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://version");
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(), APIPermission::kTabCaptureForTab));
EXPECT_TRUE(IsBlocked(extension_with_tab_capture, internal, tab_id() + 1));
EXPECT_FALSE(permissions_data->HasAPIPermissionForTab(
tab_id() + 1, APIPermission::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 defined(OS_CHROMEOS)
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);
// 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<WallpaperControllerClient>();
wallpaper_controller_client_->InitForTesting(
test_wallpaper_controller_.CreateInterfacePtr());
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();
chromeos::ChromeUserManagerImpl::ResetPublicAccountDelegatesForTesting();
chromeos::ChromeUserManager::Get()->Shutdown();
ActiveTabTest::TearDown();
}
std::unique_ptr<ScopedTestingLocalState> local_state_;
TestWallpaperController test_wallpaper_controller_;
std::unique_ptr<WallpaperControllerClient> 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::BindRepeating([](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 // defined(OS_CHROMEOS)
// An active tab test that includes an ExtensionService.
class ActiveTabWithServiceTest : public ExtensionServiceTestBase {
public:
ActiveTabWithServiceTest() {}
void SetUp() override;
void TearDown() override;
private:
DISALLOW_COPY_AND_ASSIGN(ActiveTabWithServiceTest);
};
void ActiveTabWithServiceTest::SetUp() {
ExtensionServiceTestBase::SetUp();
content::BrowserSideNavigationSetUp();
}
void ActiveTabWithServiceTest::TearDown() {
content::BrowserSideNavigationTearDown();
ExtensionServiceTestBase::TearDown();
}
// 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 = 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));
permission_granter->GrantIfRequested(extension.get());
EXPECT_FALSE(extension->permissions_data()->CanCaptureVisiblePage(
web_contents->GetLastCommittedURL(), tab_id, nullptr));
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));
permission_granter->GrantIfRequested(extension.get());
EXPECT_TRUE(extension->permissions_data()->CanCaptureVisiblePage(
web_contents->GetLastCommittedURL(), tab_id, nullptr));
}
} // namespace
} // namespace extensions