OOR-CORS: Origin access list support for Chrome Extensions
This patch makes Chrome Extensions to call BrowserContext
functions to have right permission access lists in all
relevant processes including NetworkService that also has
CORS checks.
Since NetworkService is accessed over mojo, all operations
need to be asynchronous. This requires that PermissionsUpdater
take a completion callback to let callers know its completion.
PermissionsUpdater now relies on a private helper class that
outlives it to handle the asynchronous completion.
Bug: 870172
Cq-Include-Trybots: luci.chromium.try:linux_mojo
Change-Id: I1c0642f162a0a71034c2529262150dbf4a1e4da8
Reviewed-on: https://chromium-review.googlesource.com/c/1186382
Commit-Queue: Takashi Toyoshima <[email protected]>
Reviewed-by: Devlin <[email protected]>
Reviewed-by: Ćukasz Anforowicz <[email protected]>
Cr-Commit-Position: refs/heads/master@{#609194}
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 7800a93..b0a9949 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -710,8 +710,9 @@
ExtensionUpdater::CheckParams params;
params.fetch_priority = ManifestFetchData::FetchPriority::FOREGROUND;
params.install_immediately = true;
- params.callback = base::BindOnce(
- &DeveloperPrivateAutoUpdateFunction::OnComplete, this /* refcounted */);
+ params.callback =
+ base::BindOnce(&DeveloperPrivateAutoUpdateFunction::OnComplete,
+ base::RetainedRef(this));
updater->CheckNow(std::move(params));
}
return RespondLater();
@@ -746,10 +747,9 @@
info_generator_.reset(new ExtensionInfoGenerator(browser_context()));
info_generator_->CreateExtensionsInfo(
- include_disabled,
- include_terminated,
+ include_disabled, include_terminated,
base::Bind(&DeveloperPrivateGetExtensionsInfoFunction::OnInfosGenerated,
- this /* refcounted */));
+ base::RetainedRef(this)));
return RespondLater();
}
@@ -777,7 +777,7 @@
info_generator_->CreateExtensionInfo(
params->id,
base::Bind(&DeveloperPrivateGetExtensionInfoFunction::OnInfosGenerated,
- this /* refcounted */));
+ base::RetainedRef(this)));
return RespondLater();
}
@@ -809,7 +809,7 @@
extension->path(), IDS_APPLICATION_INFO_SIZE_SMALL_LABEL,
base::BindOnce(
&DeveloperPrivateGetExtensionSizeFunction::OnSizeCalculated,
- this /* refcounted */));
+ base::RetainedRef(this)));
return RespondLater();
}
@@ -829,10 +829,9 @@
info_generator_.reset(new ExtensionInfoGenerator(browser_context()));
info_generator_->CreateExtensionsInfo(
- params->include_disabled,
- params->include_terminated,
+ params->include_disabled, params->include_terminated,
base::Bind(&DeveloperPrivateGetItemsInfoFunction::OnInfosGenerated,
- this /* refcounted */));
+ base::RetainedRef(this)));
return RespondLater();
}
@@ -2015,9 +2014,16 @@
.GrantRuntimePermissions(
*extension,
PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- new_host_permissions, new_host_permissions));
+ new_host_permissions, new_host_permissions),
+ base::BindOnce(&DeveloperPrivateAddHostPermissionFunction::
+ OnRuntimePermissionsGranted,
+ base::RetainedRef(this)));
- return RespondNow(NoArguments());
+ return did_respond() ? AlreadyResponded() : RespondLater();
+}
+
+void DeveloperPrivateAddHostPermissionFunction::OnRuntimePermissionsGranted() {
+ Respond(NoArguments());
}
DeveloperPrivateRemoveHostPermissionFunction::
@@ -2055,8 +2061,18 @@
return RespondNow(Error("Cannot remove a host that hasn't been granted."));
PermissionsUpdater(browser_context())
- .RevokeRuntimePermissions(*extension, *permissions_to_remove);
- return RespondNow(NoArguments());
+ .RevokeRuntimePermissions(
+ *extension, *permissions_to_remove,
+ base::BindOnce(&DeveloperPrivateRemoveHostPermissionFunction::
+ OnRuntimePermissionsRevoked,
+ base::RetainedRef(this)));
+
+ return did_respond() ? AlreadyResponded() : RespondLater();
+}
+
+void DeveloperPrivateRemoveHostPermissionFunction::
+ OnRuntimePermissionsRevoked() {
+ Respond(NoArguments());
}
} // namespace api
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h
index 373a430..eb31145 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -781,6 +781,8 @@
ResponseAction Run() override;
+ void OnRuntimePermissionsGranted();
+
DISALLOW_COPY_AND_ASSIGN(DeveloperPrivateAddHostPermissionFunction);
};
@@ -796,6 +798,8 @@
ResponseAction Run() override;
+ void OnRuntimePermissionsRevoked();
+
DISALLOW_COPY_AND_ASSIGN(DeveloperPrivateRemoveHostPermissionFunction);
};
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index d02d45eb..65a51a9 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -21,6 +21,7 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/ui/browser.h"
@@ -1437,9 +1438,10 @@
URLPattern new_pattern(Extension::kValidHostPermissionSchemes,
"https://*.google.com/*");
- PermissionsUpdater(profile()).GrantRuntimePermissions(
- *extension, PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- URLPatternSet({new_pattern}), URLPatternSet()));
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension,
+ PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
+ URLPatternSet({new_pattern}), URLPatternSet()));
const GURL kGoogleCom("https://google.com/");
const GURL kMapsGoogleCom("https://maps.google.com/");
@@ -1643,8 +1645,9 @@
"https://example.com/*")});
PermissionSet permissions(APIPermissionSet(), ManifestPermissionSet(), hosts,
hosts);
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- permissions);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, permissions);
+
// The event router fetches icons from a blocking thread when sending the
// update event; allow it to finish before verifying the event was dispatched.
base::RunLoop().RunUntilIdle();
@@ -1653,8 +1656,8 @@
test_observer.ClearEvents();
- PermissionsUpdater(profile()).RevokeRuntimePermissions(*extension,
- permissions);
+ permissions_test_util::RevokeRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, permissions);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
WasPermissionsUpdatedEventDispatched(test_observer, extension->id()));
@@ -1688,8 +1691,9 @@
apis.insert(APIPermission::kTab);
PermissionSet permissions(apis, ManifestPermissionSet(), URLPatternSet(),
URLPatternSet());
- PermissionsUpdater(profile()).GrantOptionalPermissions(*dummy_extension,
- permissions);
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *dummy_extension, permissions);
+
// The event router fetches icons from a blocking thread when sending the
// update event; allow it to finish before verifying the event was dispatched.
base::RunLoop().RunUntilIdle();
@@ -1698,8 +1702,9 @@
test_observer.ClearEvents();
- PermissionsUpdater(profile()).RevokeOptionalPermissions(
- *dummy_extension, permissions, PermissionsUpdater::REMOVE_HARD);
+ permissions_test_util::RevokeOptionalPermissionsAndWaitForCompletion(
+ profile(), *dummy_extension, permissions,
+ PermissionsUpdater::REMOVE_HARD);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(WasPermissionsUpdatedEventDispatched(test_observer,
dummy_extension->id()));
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
index a49331ea3..f5f29846 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
@@ -23,6 +23,7 @@
#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/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/profiles/profile.h"
@@ -520,8 +521,8 @@
PermissionSet all_chromium_set(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({all_chromium}),
URLPatternSet({all_chromium}));
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- all_chromium_set);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, all_chromium_set);
// The extension should only be granted http://chromium.org/* (since that's
// the intersection with what it requested).
@@ -572,8 +573,8 @@
PermissionSet all_chromium_set(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({all_chromium}),
URLPatternSet({all_chromium}));
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- all_chromium_set);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, all_chromium_set);
// The generated info should use the entirety of the granted permission,
// which is *://chromium.org/*.
@@ -653,8 +654,8 @@
PermissionSet example_com_set(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({example_com}),
URLPatternSet({example_com}));
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- example_com_set);
+ PermissionsUpdater(profile()).GrantRuntimePermissions(
+ *extension, example_com_set, base::DoNothing::Once());
}
{
@@ -681,8 +682,8 @@
PermissionSet example_com_set(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({example_com}),
URLPatternSet({example_com}));
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- example_com_set);
+ PermissionsUpdater(profile()).GrantRuntimePermissions(
+ *extension, example_com_set, base::DoNothing::Once());
}
{
diff --git a/chrome/browser/extensions/api/permissions/permissions_api.cc b/chrome/browser/extensions/api/permissions/permissions_api.cc
index 1aa8da56..b39ab35 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api.cc
@@ -6,6 +6,7 @@
#include <memory>
+#include "base/bind.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
#include "chrome/browser/extensions/chrome_extension_function_details.h"
@@ -126,10 +127,12 @@
*permissions, extension()->permissions_data()->active_permissions());
PermissionsUpdater(browser_context())
- .RevokeOptionalPermissions(*extension(), *permissions,
- PermissionsUpdater::REMOVE_SOFT);
- return RespondNow(
- ArgumentList(api::permissions::Remove::Results::Create(true)));
+ .RevokeOptionalPermissions(
+ *extension(), *permissions, PermissionsUpdater::REMOVE_SOFT,
+ base::BindOnce(
+ &PermissionsRemoveFunction::Respond, base::RetainedRef(this),
+ ArgumentList(api::permissions::Remove::Results::Create(true))));
+ return did_respond() ? AlreadyResponded() : RespondLater();
}
// static
@@ -237,8 +240,6 @@
new_permissions = PermissionSet::CreateDifference(
*new_permissions, *already_granted_permissions);
- AddRef(); // Balanced in OnInstallPromptDone().
-
// We don't need to show the prompt if there are no new warnings, or if
// we're skipping the confirmation UI. COMPONENT extensions are allowed to
// silently increase their permission level.
@@ -255,7 +256,7 @@
.empty();
if (has_no_warnings || extension_->location() == Manifest::COMPONENT) {
OnInstallPromptDone(ExtensionInstallPrompt::Result::ACCEPTED);
- return AlreadyResponded();
+ return did_respond() ? AlreadyResponded() : RespondLater();
}
// Otherwise, we have to prompt the user (though we might "autoconfirm" for a
@@ -266,13 +267,14 @@
OnInstallPromptDone(ExtensionInstallPrompt::Result::ACCEPTED);
else if (auto_confirm_for_tests == ABORT)
OnInstallPromptDone(ExtensionInstallPrompt::Result::USER_CANCELED);
- return AlreadyResponded();
+ return did_respond() ? AlreadyResponded() : RespondLater();
}
install_ui_.reset(new ExtensionInstallPrompt(
Profile::FromBrowserContext(browser_context()), native_window));
install_ui_->ShowDialog(
- base::Bind(&PermissionsRequestFunction::OnInstallPromptDone, this),
+ base::Bind(&PermissionsRequestFunction::OnInstallPromptDone,
+ base::RetainedRef(this)),
extension(), nullptr,
std::make_unique<ExtensionInstallPrompt::Prompt>(
ExtensionInstallPrompt::PERMISSIONS_PROMPT),
@@ -287,17 +289,46 @@
void PermissionsRequestFunction::OnInstallPromptDone(
ExtensionInstallPrompt::Result result) {
- bool granted = result == ExtensionInstallPrompt::Result::ACCEPTED;
- if (granted) {
- PermissionsUpdater permissions_updater(browser_context());
- permissions_updater.GrantRuntimePermissions(*extension(),
- *requested_withheld_);
- permissions_updater.GrantOptionalPermissions(*extension(),
- *requested_optional_);
+ if (result != ExtensionInstallPrompt::Result::ACCEPTED) {
+ Respond(ArgumentList(api::permissions::Request::Results::Create(false)));
+ return;
}
+ PermissionsUpdater permissions_updater(browser_context());
+ if (!requested_withheld_->IsEmpty()) {
+ requesting_withheld_permissions_ = true;
+ permissions_updater.GrantRuntimePermissions(
+ *extension(), *requested_withheld_,
+ base::BindOnce(&PermissionsRequestFunction::OnRuntimePermissionsGranted,
+ base::RetainedRef(this)));
+ }
+ if (!requested_optional_->IsEmpty()) {
+ requesting_optional_permissions_ = true;
+ permissions_updater.GrantOptionalPermissions(
+ *extension(), *requested_optional_,
+ base::BindOnce(
+ &PermissionsRequestFunction::OnOptionalPermissionsGranted,
+ base::RetainedRef(this)));
+ }
+ // Grant{Runtime|Optional}Permissions calls above can finish synchronously.
+ if (!did_respond())
+ RespondIfRequestsFinished();
+}
- Respond(ArgumentList(api::permissions::Request::Results::Create(granted)));
- Release(); // Balanced in Run().
+void PermissionsRequestFunction::OnRuntimePermissionsGranted() {
+ requesting_withheld_permissions_ = false;
+ RespondIfRequestsFinished();
+}
+
+void PermissionsRequestFunction::OnOptionalPermissionsGranted() {
+ requesting_optional_permissions_ = false;
+ RespondIfRequestsFinished();
+}
+
+void PermissionsRequestFunction::RespondIfRequestsFinished() {
+ if (requesting_withheld_permissions_ || requesting_optional_permissions_)
+ return;
+
+ Respond(ArgumentList(api::permissions::Request::Results::Create(true)));
}
std::unique_ptr<const PermissionSet>
diff --git a/chrome/browser/extensions/api/permissions/permissions_api.h b/chrome/browser/extensions/api/permissions/permissions_api.h
index 9e21613..87c04d9 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api.h
+++ b/chrome/browser/extensions/api/permissions/permissions_api.h
@@ -74,6 +74,9 @@
private:
void OnInstallPromptDone(ExtensionInstallPrompt::Result result);
+ void OnRuntimePermissionsGranted();
+ void OnOptionalPermissionsGranted();
+ void RespondIfRequestsFinished();
std::unique_ptr<ExtensionInstallPrompt> install_ui_;
@@ -82,6 +85,9 @@
// Requested permissions that are currently optional, and not granted.
std::unique_ptr<const PermissionSet> requested_optional_;
+ bool requesting_withheld_permissions_ = false;
+ bool requesting_optional_permissions_ = false;
+
// The permissions, if any, that Chrome would prompt the user for. This will
// be recorded if and only if the prompt is being bypassed for a test (see
// also SetAutoConfirmForTests()).
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
index ff0e5934..5b1fc5c 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "chrome/browser/extensions/api/permissions/permissions_api.h"
+
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/test/scoped_feature_list.h"
@@ -11,6 +12,7 @@
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/test/base/testing_profile.h"
@@ -348,8 +350,8 @@
PermissionSet permissions(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({chromium_org_pattern}),
URLPatternSet());
- PermissionsUpdater(profile()).RevokeRuntimePermissions(*extension,
- permissions);
+ permissions_test_util::RevokeRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, permissions);
}
EXPECT_TRUE(
permissions_data->active_permissions().effective_hosts().is_empty());
diff --git a/chrome/browser/extensions/extension_context_menu_model_unittest.cc b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
index f4e17d3..d4711ba 100644
--- a/chrome/browser/extensions/extension_context_menu_model_unittest.cc
+++ b/chrome/browser/extensions/extension_context_menu_model_unittest.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/menu_manager.h"
#include "chrome/browser/extensions/menu_manager_factory.h"
+#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/ui/browser.h"
@@ -1062,9 +1063,10 @@
if (test_case.granted_pattern) {
URLPattern pattern(UserScript::ValidUserScriptSchemes(false),
*test_case.granted_pattern);
- PermissionsUpdater(profile()).GrantRuntimePermissions(
- *extension, PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- URLPatternSet(), URLPatternSet({pattern})));
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension,
+ PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
+ URLPatternSet(), URLPatternSet({pattern})));
}
web_contents_tester->NavigateAndCommit(test_case.current_url);
@@ -1184,8 +1186,8 @@
// However, if the extension has runtime-granted permissions to b.com, we
// *should* display them in the menu.
- PermissionsUpdater(profile()).GrantRuntimePermissions(*extension,
- b_com_permissions);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, b_com_permissions);
{
ExtensionContextMenuModel menu(extension.get(), GetBrowser(),
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index c1177d09..52e01ba 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -63,6 +63,7 @@
#include "chrome/browser/extensions/pack_extension_job.h"
#include "chrome/browser/extensions/pending_extension_info.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
+#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/test_blacklist.h"
#include "chrome/browser/extensions/test_extension_system.h"
@@ -616,9 +617,8 @@
const Extension* extension = service()->GetInstalledExtension(id);
const PermissionSet& all_optional_permissions =
PermissionsParser::GetOptionalPermissions(extension);
- PermissionsUpdater perms_updater(profile());
- perms_updater.GrantOptionalPermissions(*extension,
- all_optional_permissions);
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, all_optional_permissions);
}
testing::AssertionResult IsBlocked(const std::string& id) {
diff --git a/chrome/browser/extensions/permission_messages_unittest.cc b/chrome/browser/extensions/permission_messages_unittest.cc
index 2ef1f6d..f07ecde6 100644
--- a/chrome/browser/extensions/permission_messages_unittest.cc
+++ b/chrome/browser/extensions/permission_messages_unittest.cc
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/test_extension_environment.h"
#include "chrome/common/extensions/permissions/chrome_permission_message_provider.h"
@@ -79,9 +80,9 @@
}
void GrantOptionalPermissions() {
- PermissionsUpdater perms_updater(env_.profile());
- perms_updater.GrantOptionalPermissions(
- *app_, PermissionsParser::GetOptionalPermissions(app_.get()));
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ env_.profile(), *app_,
+ PermissionsParser::GetOptionalPermissions(app_.get()));
}
std::vector<base::string16> active_permissions() {
diff --git a/chrome/browser/extensions/permissions_test_util.cc b/chrome/browser/extensions/permissions_test_util.cc
new file mode 100644
index 0000000..ca3fa79
--- /dev/null
+++ b/chrome/browser/extensions/permissions_test_util.cc
@@ -0,0 +1,58 @@
+// Copyright 2018 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 "chrome/browser/extensions/permissions_test_util.h"
+
+#include "base/run_loop.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/permission_set.h"
+
+namespace extensions {
+namespace permissions_test_util {
+
+void GrantOptionalPermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions) {
+ base::RunLoop run_loop;
+ PermissionsUpdater(browser_context)
+ .GrantOptionalPermissions(extension, permissions, run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+void GrantRuntimePermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions) {
+ base::RunLoop run_loop;
+ PermissionsUpdater(browser_context)
+ .GrantRuntimePermissions(extension, permissions, run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+void RevokeOptionalPermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions,
+ PermissionsUpdater::RemoveType remove_type) {
+ base::RunLoop run_loop;
+ PermissionsUpdater(browser_context)
+ .RevokeOptionalPermissions(extension, permissions, remove_type,
+ run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+void RevokeRuntimePermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions) {
+ base::RunLoop run_loop;
+ PermissionsUpdater(browser_context)
+ .RevokeRuntimePermissions(extension, permissions, run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+} // namespace browsertest_util
+} // namespace extensions
diff --git a/chrome/browser/extensions/permissions_test_util.h b/chrome/browser/extensions/permissions_test_util.h
new file mode 100644
index 0000000..2285ce4
--- /dev/null
+++ b/chrome/browser/extensions/permissions_test_util.h
@@ -0,0 +1,44 @@
+// Copyright 2018 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_PERMISSIONS_TEST_UTIL_H_
+#define CHROME_BROWSER_EXTENSIONS_PERMISSIONS_TEST_UTIL_H_
+
+#include "chrome/browser/extensions/permissions_updater.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace extensions {
+
+class Extension;
+class PermissionSet;
+
+namespace permissions_test_util {
+
+// Calls corresponding PermissionsUpdater method respectively and wait for its
+// asynchronous completion.
+void GrantOptionalPermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions);
+void GrantRuntimePermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions);
+void RevokeOptionalPermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions,
+ PermissionsUpdater::RemoveType remove_type);
+void RevokeRuntimePermissionsAndWaitForCompletion(
+ content::BrowserContext* browser_context,
+ const Extension& extension,
+ const PermissionSet& permissions);
+
+} // namespace permissions_test_util
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_PERMISSIONS_TEST_UTIL_H_
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index a04611ab..26293f95 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -7,28 +7,37 @@
#include <set>
#include <utility>
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
#include "chrome/browser/extensions/extension_management.h"
+#include "chrome/browser/extensions/extension_system_factory.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/permissions.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
+#include "content/public/browser/browser_context.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/event_router.h"
+#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/notification_types.h"
+#include "extensions/common/cors_util.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
+#include "services/network/public/cpp/features.h"
using content::RenderProcessHost;
using extensions::permissions_api_helpers::PackPermissionSet;
@@ -84,16 +93,137 @@
return GetDelegateWrapper().get();
}
+// A helper class to watch profile lifetime.
+class PermissionsUpdaterShutdownNotifierFactory
+ : public BrowserContextKeyedServiceShutdownNotifierFactory {
+ public:
+ static PermissionsUpdaterShutdownNotifierFactory* GetInstance() {
+ static base::NoDestructor<PermissionsUpdaterShutdownNotifierFactory>
+ factory;
+ return factory.get();
+ }
+
+ private:
+ friend class base::NoDestructor<PermissionsUpdaterShutdownNotifierFactory>;
+
+ PermissionsUpdaterShutdownNotifierFactory()
+ : BrowserContextKeyedServiceShutdownNotifierFactory(
+ "PermissionsUpdaterShutdownFactory") {
+ DependsOn(EventRouterFactory::GetInstance());
+ DependsOn(ExtensionSystemFactory::GetInstance());
+ }
+ ~PermissionsUpdaterShutdownNotifierFactory() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(PermissionsUpdaterShutdownNotifierFactory);
+};
+
} // namespace
-PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
- : browser_context_(browser_context), init_flag_(INIT_FLAG_NONE) {
+// A helper class to asynchronously dispatch the permissions updated
+// notification once origin access has been updated. This will fire the
+// event if and only if the BrowserContext is still valid.
+// This class manages its own lifetime and deletes itself when either the
+// permissions updated event is fired, or the BrowserContext is shut down
+// (whichever happens first).
+class PermissionsUpdater::NetworkPermissionsUpdateHelper {
+ public:
+ static void UpdateNetworkServicePermissions(
+ content::BrowserContext* browser_context,
+ EventType event_type,
+ scoped_refptr<const Extension> extension,
+ const PermissionSet& changed,
+ base::OnceClosure completion_callback);
+
+ private:
+ // This class manages its own lifetime.
+ NetworkPermissionsUpdateHelper(content::BrowserContext* browser_context,
+ base::OnceClosure dispatch_event);
+ ~NetworkPermissionsUpdateHelper();
+
+ void OnShutdown();
+ void OnOriginAccessUpdated();
+
+ base::OnceClosure dispatch_event_;
+ std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
+ shutdown_subscription_;
+ base::WeakPtrFactory<NetworkPermissionsUpdateHelper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkPermissionsUpdateHelper);
+};
+
+// static
+void PermissionsUpdater::NetworkPermissionsUpdateHelper::
+ UpdateNetworkServicePermissions(content::BrowserContext* browser_context,
+ EventType event_type,
+ scoped_refptr<const Extension> extension,
+ const PermissionSet& changed,
+ base::OnceClosure completion_callback) {
+ if (changed.effective_hosts().is_empty()) {
+ // If there is no difference in allowlist/blocklist for the extension, we
+ // can synchronously finish it without updating the CORS access list.
+ PermissionsUpdater::NotifyPermissionsUpdated(
+ browser_context, event_type, std::move(extension), changed.Clone(),
+ std::move(completion_callback));
+ return;
+ }
+
+ std::vector<network::mojom::CorsOriginPatternPtr> allow_list =
+ CreateCorsOriginAccessAllowList(*extension);
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ ExtensionsClient::Get()->AddOriginAccessPermissions(*extension, true,
+ &allow_list);
+ }
+
+ NetworkPermissionsUpdateHelper* helper = new NetworkPermissionsUpdateHelper(
+ browser_context,
+ base::BindOnce(&PermissionsUpdater::NotifyPermissionsUpdated,
+ browser_context, event_type, extension,
+ changed.Clone(), std::move(completion_callback)));
+
+ // After an asynchronous call below, the helper will call
+ // NotifyPermissionsUpdated if the profile is still valid.
+ content::BrowserContext::SetCorsOriginAccessListsForOrigin(
+ browser_context, url::Origin::Create(extension->url()),
+ std::move(allow_list), CreateCorsOriginAccessBlockList(*extension),
+ base::BindOnce(&NetworkPermissionsUpdateHelper::OnOriginAccessUpdated,
+ helper->weak_factory_.GetWeakPtr()));
}
+PermissionsUpdater::NetworkPermissionsUpdateHelper::
+ NetworkPermissionsUpdateHelper(content::BrowserContext* browser_context,
+ base::OnceClosure dispatch_event)
+ : dispatch_event_(std::move(dispatch_event)),
+ shutdown_subscription_(
+ PermissionsUpdaterShutdownNotifierFactory::GetInstance()
+ ->Get(browser_context)
+ ->Subscribe(
+ base::Bind(&NetworkPermissionsUpdateHelper::OnShutdown,
+ base::Unretained(this)))),
+ weak_factory_(this) {}
+
+PermissionsUpdater::NetworkPermissionsUpdateHelper::
+ ~NetworkPermissionsUpdateHelper() {}
+
+void PermissionsUpdater::NetworkPermissionsUpdateHelper::OnShutdown() {
+ // The profile is shutting down. Don't dispatch the permissions updated
+ // event, and clean up the dangling references.
+ delete this;
+}
+
+void PermissionsUpdater::NetworkPermissionsUpdateHelper::
+ OnOriginAccessUpdated() {
+ // The origin access list was successfully updated; dispatch the event
+ // and clean up dangling references.
+ std::move(dispatch_event_).Run();
+ delete this;
+}
+
+PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
+ : PermissionsUpdater(browser_context, INIT_FLAG_NONE) {}
+
PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context,
InitFlag init_flag)
- : browser_context_(browser_context), init_flag_(init_flag) {
-}
+ : browser_context_(browser_context), init_flag_(init_flag) {}
PermissionsUpdater::~PermissionsUpdater() {}
@@ -105,7 +235,8 @@
void PermissionsUpdater::GrantOptionalPermissions(
const Extension& extension,
- const PermissionSet& permissions) {
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback) {
// TODO(devlin): Ideally, we'd have this CHECK in place, but unit tests are
// currently violating it.
// CHECK(PermissionsParser::GetOptionalPermissions(&extension).Contains(
@@ -123,12 +254,13 @@
constexpr int permissions_store_mask =
kActivePermissions | kGrantedPermissions | kRuntimeGrantedPermissions;
AddPermissionsImpl(extension, permissions, permissions_store_mask,
- permissions);
+ permissions, std::move(completion_callback));
}
void PermissionsUpdater::GrantRuntimePermissions(
const Extension& extension,
- const PermissionSet& permissions) {
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback) {
DCHECK(base::FeatureList::IsEnabled(
extensions_features::kRuntimeHostPermissions));
@@ -161,13 +293,15 @@
// enabled.
constexpr int permissions_store_mask = kRuntimeGrantedPermissions;
AddPermissionsImpl(extension, *active_permissions_to_add,
- permissions_store_mask, permissions);
+ permissions_store_mask, permissions,
+ std::move(completion_callback));
}
void PermissionsUpdater::RevokeOptionalPermissions(
const Extension& extension,
const PermissionSet& permissions,
- RemoveType remove_type) {
+ RemoveType remove_type,
+ base::OnceClosure completion_callback) {
// TODO(devlin): Ideally, we'd have this CHECK in place, but unit tests are
// currently violating it.
// CHECK(PermissionsParser::GetOptionalPermissions(&extension).Contains(
@@ -185,12 +319,13 @@
permissions_store_mask |= kGrantedPermissions | kRuntimeGrantedPermissions;
RemovePermissionsImpl(extension, permissions, permissions_store_mask,
- permissions);
+ permissions, std::move(completion_callback));
}
void PermissionsUpdater::RevokeRuntimePermissions(
const Extension& extension,
- const PermissionSet& permissions) {
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback) {
DCHECK(base::FeatureList::IsEnabled(
extensions_features::kRuntimeHostPermissions));
// Similar to the process in adding permissions, we might be revoking more
@@ -239,7 +374,8 @@
// to the active permissions stored in prefs, they are also not removed.
constexpr int permissions_store_mask = kRuntimeGrantedPermissions;
RemovePermissionsImpl(extension, *active_permissions_to_remove,
- permissions_store_mask, permissions);
+ permissions_store_mask, permissions,
+ std::move(completion_callback));
}
void PermissionsUpdater::SetPolicyHostRestrictions(
@@ -249,17 +385,19 @@
extension->permissions_data()->SetPolicyHostRestrictions(
runtime_blocked_hosts, runtime_allowed_hosts);
- // Send notification to the currently running renderers of the runtime block
- // hosts settings.
- const PermissionSet perms;
- NotifyPermissionsUpdated(POLICY, extension, perms);
+ // Update the BrowserContext origin lists, and send notification to the
+ // currently running renderers of the runtime block hosts settings.
+ NetworkPermissionsUpdateHelper::UpdateNetworkServicePermissions(
+ browser_context_, POLICY, extension, PermissionSet(),
+ base::DoNothing::Once());
}
void PermissionsUpdater::SetUsesDefaultHostRestrictions(
const Extension* extension) {
extension->permissions_data()->SetUsesDefaultHostRestrictions();
- const PermissionSet perms;
- NotifyPermissionsUpdated(POLICY, extension, perms);
+ NetworkPermissionsUpdateHelper::UpdateNetworkServicePermissions(
+ browser_context_, POLICY, extension, PermissionSet(),
+ base::DoNothing::Once());
}
void PermissionsUpdater::SetDefaultPolicyHostRestrictions(
@@ -292,7 +430,9 @@
// permissions would be re-added.
constexpr bool update_active_prefs = true;
SetPermissions(extension, std::move(total), update_active_prefs);
- NotifyPermissionsUpdated(REMOVED, extension, *successfully_removed);
+ NetworkPermissionsUpdateHelper::UpdateNetworkServicePermissions(
+ browser_context_, REMOVED, extension, *successfully_removed,
+ base::DoNothing::Once());
}
std::unique_ptr<const PermissionSet>
@@ -369,7 +509,8 @@
void PermissionsUpdater::AddPermissionsForTesting(
const Extension& extension,
const PermissionSet& permissions) {
- AddPermissionsImpl(extension, permissions, kNone, permissions);
+ AddPermissionsImpl(extension, permissions, kNone, permissions,
+ base::DoNothing::Once());
}
void PermissionsUpdater::SetPermissions(
@@ -408,37 +549,22 @@
}
}
-void PermissionsUpdater::DispatchEvent(
- const std::string& extension_id,
- events::HistogramValue histogram_value,
- const char* event_name,
- const PermissionSet& changed_permissions) {
- EventRouter* event_router = EventRouter::Get(browser_context_);
- if (!event_router)
- return;
-
- std::unique_ptr<base::ListValue> value(new base::ListValue());
- std::unique_ptr<api::permissions::Permissions> permissions =
- PackPermissionSet(changed_permissions);
- value->Append(permissions->ToValue());
- auto event = std::make_unique<Event>(histogram_value, event_name,
- std::move(value), browser_context_);
- event_router->DispatchEventToExtension(extension_id, std::move(event));
-}
-
+// static
void PermissionsUpdater::NotifyPermissionsUpdated(
+ content::BrowserContext* browser_context,
EventType event_type,
- const Extension* extension,
- const PermissionSet& changed) {
- DCHECK_EQ(0, init_flag_ & INIT_FLAG_TRANSIENT);
-
- if (changed.IsEmpty() && event_type != POLICY)
+ scoped_refptr<const Extension> extension,
+ std::unique_ptr<const PermissionSet> changed,
+ base::OnceClosure completion_callback) {
+ if (changed->IsEmpty() && event_type != POLICY) {
+ std::move(completion_callback).Run();
return;
+ }
UpdatedExtensionPermissionsInfo::Reason reason;
events::HistogramValue histogram_value = events::UNKNOWN;
const char* event_name = NULL;
- Profile* profile = Profile::FromBrowserContext(browser_context_);
+ Profile* profile = Profile::FromBrowserContext(browser_context);
if (event_type == REMOVED) {
reason = UpdatedExtensionPermissionsInfo::REMOVED;
@@ -455,7 +581,7 @@
// Notify other APIs or interested parties.
UpdatedExtensionPermissionsInfo info =
- UpdatedExtensionPermissionsInfo(extension, changed, reason);
+ UpdatedExtensionPermissionsInfo(extension.get(), *changed, reason);
content::NotificationService::current()->Notify(
extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
content::Source<Profile>(profile),
@@ -488,8 +614,19 @@
// Trigger the onAdded and onRemoved events in the extension. We explicitly
// don't do this for policy-related events.
- if (event_name)
- DispatchEvent(extension->id(), histogram_value, event_name, changed);
+ EventRouter* event_router =
+ event_name ? EventRouter::Get(browser_context) : nullptr;
+ if (event_router) {
+ std::unique_ptr<base::ListValue> value(new base::ListValue());
+ std::unique_ptr<api::permissions::Permissions> permissions =
+ PackPermissionSet(*changed);
+ value->Append(permissions->ToValue());
+ auto event = std::make_unique<Event>(histogram_value, event_name,
+ std::move(value), browser_context);
+ event_router->DispatchEventToExtension(extension->id(), std::move(event));
+ }
+
+ std::move(completion_callback).Run();
}
// Notify the renderers that extension policy (policy_blocked_hosts) is updated
@@ -521,7 +658,8 @@
const Extension& extension,
const PermissionSet& active_permissions_to_add,
int permissions_store_mask,
- const PermissionSet& prefs_permissions_to_add) {
+ const PermissionSet& prefs_permissions_to_add,
+ base::OnceClosure completion_callback) {
std::unique_ptr<const PermissionSet> new_active = PermissionSet::CreateUnion(
active_permissions_to_add,
extension.permissions_data()->active_permissions());
@@ -542,14 +680,17 @@
prefs_permissions_to_add);
}
- NotifyPermissionsUpdated(ADDED, &extension, active_permissions_to_add);
+ NetworkPermissionsUpdateHelper::UpdateNetworkServicePermissions(
+ browser_context_, ADDED, &extension, active_permissions_to_add,
+ std::move(completion_callback));
}
void PermissionsUpdater::RemovePermissionsImpl(
const Extension& extension,
const PermissionSet& active_permissions_to_remove,
int permissions_store_mask,
- const PermissionSet& prefs_permissions_to_remove) {
+ const PermissionSet& prefs_permissions_to_remove,
+ base::OnceClosure completion_callback) {
std::unique_ptr<const PermissionSet> new_active =
PermissionSet::CreateDifference(
extension.permissions_data()->active_permissions(),
@@ -571,7 +712,9 @@
prefs_permissions_to_remove);
}
- NotifyPermissionsUpdated(REMOVED, &extension, active_permissions_to_remove);
+ NetworkPermissionsUpdateHelper::UpdateNetworkServicePermissions(
+ browser_context_, REMOVED, &extension, active_permissions_to_remove,
+ std::move(completion_callback));
}
} // namespace extensions
diff --git a/chrome/browser/extensions/permissions_updater.h b/chrome/browser/extensions/permissions_updater.h
index d70b027..886a66c9 100644
--- a/chrome/browser/extensions/permissions_updater.h
+++ b/chrome/browser/extensions/permissions_updater.h
@@ -8,7 +8,9 @@
#include <memory>
#include <string>
+#include "base/callback_forward.h"
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "extensions/browser/extension_event_histogram_value.h"
namespace content {
@@ -36,6 +38,13 @@
std::unique_ptr<const PermissionSet>* granted_permissions) = 0;
};
+ // If INIT_FLAG_TRANSIENT is specified, this updater is being used for an
+ // extension that is not actually installed (and instead is just being
+ // initialized e.g. to display the permission warnings in an install prompt).
+ // In these cases, this updater should follow all rules below.
+ // a) don't check prefs for stored permissions.
+ // b) don't send notifications of permission changes, because there is no
+ // installed extension that would be affected.
enum InitFlag {
INIT_FLAG_NONE = 0,
INIT_FLAG_TRANSIENT = 1 << 0,
@@ -73,7 +82,8 @@
// NOTE: This should only be used for granting permissions defined in the
// extension's optional permissions set through the permissions API.
void GrantOptionalPermissions(const Extension& extension,
- const PermissionSet& permissions);
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback);
// Grants |permissions| that were withheld at installation and granted at
// runtime to |extension|, updating the active permission set and notifying
@@ -84,7 +94,8 @@
// NOTE: This should only be used for granting permissions through the runtime
// host permissions feature.
void GrantRuntimePermissions(const Extension& extension,
- const PermissionSet& permissions);
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback);
// Removes |permissions| that were defined as optional in the manifest from
// the |extension|, updating the active permission set and notifying any
@@ -95,7 +106,8 @@
// extension's optional permissions set through the permissions API.
void RevokeOptionalPermissions(const Extension& extension,
const PermissionSet& permissions,
- RemoveType remove_type);
+ RemoveType remove_type,
+ base::OnceClosure completion_callback);
// Removes |permissions| that were withheld at installation and granted at
// runtime from |extension|, updating the active permission set and notifying
@@ -103,7 +115,8 @@
// NOTE: This should only be used for removing permissions through the runtime
// host permissions feature.
void RevokeRuntimePermissions(const Extension& extension,
- const PermissionSet& permissions);
+ const PermissionSet& permissions,
+ base::OnceClosure completion_callback);
// Removes the |permissions| from |extension| and makes no effort to determine
// if doing so is safe in the slightlest. This method shouldn't be used,
@@ -147,6 +160,8 @@
const PermissionSet& permissions);
private:
+ class NetworkPermissionsUpdateHelper;
+
enum EventType {
ADDED,
REMOVED,
@@ -161,6 +176,18 @@
kActivePermissions = 1 << 2,
};
+ // Issues the relevant events, messages and notifications when the
+ // |extension|'s permissions have |changed| (|changed| is the delta).
+ // Specifically, this sends the EXTENSION_PERMISSIONS_UPDATED notification,
+ // the ExtensionMsg_UpdatePermissions IPC message, and fires the
+ // onAdded/onRemoved events in the extension.
+ static void NotifyPermissionsUpdated(
+ content::BrowserContext* browser_context,
+ EventType event_type,
+ scoped_refptr<const Extension> extension,
+ std::unique_ptr<const PermissionSet> changed,
+ base::OnceClosure completion_callback);
+
// Sets the |extension|'s active permissions to |active|, and calculates and
// sets the |extension|'s new withheld permissions. If |update_prefs| is true,
// also updates the set of active permissions in the extension preferences.
@@ -168,21 +195,6 @@
std::unique_ptr<const PermissionSet> active,
bool update_prefs);
- // Dispatches specified event to the extension.
- void DispatchEvent(const std::string& extension_id,
- events::HistogramValue histogram_value,
- const char* event_name,
- const PermissionSet& changed_permissions);
-
- // Issues the relevant events, messages and notifications when the
- // |extension|'s permissions have |changed| (|changed| is the delta).
- // Specifically, this sends the EXTENSION_PERMISSIONS_UPDATED notification,
- // the ExtensionMsg_UpdatePermissions IPC message, and fires the
- // onAdded/onRemoved events in the extension.
- void NotifyPermissionsUpdated(EventType event_type,
- const Extension* extension,
- const PermissionSet& changed);
-
// Issues the relevant events, messages and notifications when the
// default scope management policy have changed.
// Specifically, this sends the ExtensionMsg_UpdateDefaultHostRestrictions
@@ -201,7 +213,8 @@
void AddPermissionsImpl(const Extension& extension,
const PermissionSet& active_permissions_to_add,
int permission_store_mask,
- const PermissionSet& prefs_permissions_to_add);
+ const PermissionSet& prefs_permissions_to_add,
+ base::OnceClosure completion_callback);
// Removes the given |active_permissions_to_remove| from |extension|'s current
// active permissions. Updates the preferences according to
@@ -210,7 +223,8 @@
void RemovePermissionsImpl(const Extension& extension,
const PermissionSet& active_permissions_to_remove,
int permission_store_mask,
- const PermissionSet& prefs_permissions_to_remove);
+ const PermissionSet& prefs_permissions_to_remove,
+ base::OnceClosure completion_callback);
// The associated BrowserContext.
content::BrowserContext* browser_context_;
diff --git a/chrome/browser/extensions/permissions_updater_unittest.cc b/chrome/browser/extensions/permissions_updater_unittest.cc
index 7e38b618..0b18887 100644
--- a/chrome/browser/extensions/permissions_updater_unittest.cc
+++ b/chrome/browser/extensions/permissions_updater_unittest.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/memory/ref_counted.h"
@@ -16,6 +17,7 @@
#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/permissions_test_util.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_test_util.h"
@@ -198,7 +200,7 @@
PermissionsUpdaterListener listener;
PermissionsUpdater(profile_.get())
- .GrantOptionalPermissions(*extension, delta);
+ .GrantOptionalPermissions(*extension, delta, base::DoNothing::Once());
listener.Wait();
@@ -233,7 +235,8 @@
PermissionsUpdaterListener listener;
PermissionsUpdater(profile_.get())
.RevokeOptionalPermissions(*extension, delta,
- PermissionsUpdater::REMOVE_SOFT);
+ PermissionsUpdater::REMOVE_SOFT,
+ base::DoNothing::Once());
listener.Wait();
// Verify that the notification was correct.
@@ -293,8 +296,8 @@
EXPECT_TRUE(updater.GetRevokablePermissions(extension.get())->IsEmpty());
// Add the optional "cookies" permission.
- updater.GrantOptionalPermissions(
- *extension, *api_permission_set(APIPermission::kCookie));
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, *api_permission_set(APIPermission::kCookie));
const PermissionsData* permissions = extension->permissions_data();
// The extension should have the permission in its active permissions and
// its granted permissions (stored in prefs). And, the permission should
@@ -307,8 +310,8 @@
->HasAPIPermission(APIPermission::kCookie));
// Repeat with "tabs".
- updater.GrantOptionalPermissions(*extension,
- *api_permission_set(APIPermission::kTab));
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, *api_permission_set(APIPermission::kTab));
EXPECT_TRUE(permissions->HasAPIPermission(APIPermission::kTab));
granted_permissions = prefs->GetGrantedPermissions(extension->id());
EXPECT_TRUE(granted_permissions->HasAPIPermission(APIPermission::kTab));
@@ -318,9 +321,9 @@
// Remove the "tabs" permission. The extension should no longer have it
// in its active or granted permissions, and it shouldn't be revokable.
// The extension should still have the "cookies" permission.
- updater.RevokeOptionalPermissions(*extension,
- *api_permission_set(APIPermission::kTab),
- PermissionsUpdater::REMOVE_HARD);
+ permissions_test_util::RevokeOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, *api_permission_set(APIPermission::kTab),
+ PermissionsUpdater::REMOVE_HARD);
EXPECT_FALSE(permissions->HasAPIPermission(APIPermission::kTab));
granted_permissions = prefs->GetGrantedPermissions(extension->id());
EXPECT_FALSE(granted_permissions->HasAPIPermission(APIPermission::kTab));
@@ -488,7 +491,8 @@
// Granting permissions should update both runtime-granted permissions and
// granted permissions.
- updater.GrantOptionalPermissions(*extension, optional_permissions);
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, optional_permissions);
EXPECT_EQ(optional_permissions,
*prefs->GetRuntimeGrantedPermissions(extension->id()));
EXPECT_EQ(optional_permissions,
@@ -497,8 +501,9 @@
// Removing permissions with REMOVE_SOFT should not remove the permission
// from runtime-granted permissions or granted permissions; this happens when
// the extension opts into lower privilege.
- updater.RevokeOptionalPermissions(*extension, optional_permissions,
- PermissionsUpdater::REMOVE_SOFT);
+ permissions_test_util::RevokeOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, optional_permissions,
+ PermissionsUpdater::REMOVE_SOFT);
EXPECT_EQ(optional_permissions,
*prefs->GetRuntimeGrantedPermissions(extension->id()));
EXPECT_EQ(optional_permissions,
@@ -510,9 +515,11 @@
// Note: we need to add back the permission first, so it shows up as a
// revokable permission.
// TODO(devlin): Inactive, but granted, permissions should be revokable.
- updater.GrantOptionalPermissions(*extension, optional_permissions);
- updater.RevokeOptionalPermissions(*extension, optional_permissions,
- PermissionsUpdater::REMOVE_HARD);
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, optional_permissions);
+ permissions_test_util::RevokeOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, optional_permissions,
+ PermissionsUpdater::REMOVE_HARD);
EXPECT_TRUE(prefs->GetRuntimeGrantedPermissions(extension->id())->IsEmpty());
EXPECT_TRUE(prefs->GetGrantedPermissions(extension->id())->IsEmpty());
}
@@ -555,7 +562,8 @@
// Granting runtime-granted permissions should update the runtime granted
// permissions store in preferences, but *not* granted permissions in
// preferences.
- updater.GrantRuntimePermissions(*extension, runtime_granted_permissions);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, runtime_granted_permissions);
EXPECT_EQ(runtime_granted_permissions,
*prefs->GetRuntimeGrantedPermissions(extension->id()));
EXPECT_EQ(*initial_granted_permissions,
@@ -564,7 +572,8 @@
// Removing runtime-granted permissions should not remove the permission
// from runtime-granted permissions; granted permissions should remain
// unchanged.
- updater.RevokeRuntimePermissions(*extension, runtime_granted_permissions);
+ permissions_test_util::RevokeRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, runtime_granted_permissions);
EXPECT_TRUE(prefs->GetRuntimeGrantedPermissions(extension->id())->IsEmpty());
EXPECT_EQ(*initial_granted_permissions,
@@ -619,7 +628,8 @@
URLPatternSet());
// Give the extension access to the test site. Now, the test site permission
// should be revokable.
- updater.GrantRuntimePermissions(*extension, permission_set);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, permission_set);
EXPECT_TRUE(extension->permissions_data()
->active_permissions()
.HasExplicitAccessToOrigin(kOrigin));
@@ -628,8 +638,8 @@
// Revoke the test site permission. The extension should no longer have
// access to test site, and the revokable permissions should be empty.
- updater.RevokeOptionalPermissions(*extension, permission_set,
- PermissionsUpdater::REMOVE_HARD);
+ permissions_test_util::RevokeOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension, permission_set, PermissionsUpdater::REMOVE_HARD);
EXPECT_FALSE(extension->permissions_data()
->active_permissions()
.HasExplicitAccessToOrigin(kOrigin));
@@ -790,7 +800,8 @@
const PermissionSet runtime_permissions(
APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({kAllGooglePattern}), URLPatternSet());
- updater.GrantRuntimePermissions(*extension, runtime_permissions);
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, runtime_permissions);
// The extension object's permission should never include un-requested
// permissions, so it should only include maps.google.com.
@@ -823,7 +834,8 @@
}
// Revoke the host permission.
- updater.RevokeRuntimePermissions(*extension, runtime_permissions);
+ permissions_test_util::RevokeRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension, runtime_permissions);
EXPECT_FALSE(extension->permissions_data()
->active_permissions()
diff --git a/chrome/browser/extensions/scripting_permissions_modifier.cc b/chrome/browser/extensions/scripting_permissions_modifier.cc
index ca31aac..de66669e 100644
--- a/chrome/browser/extensions/scripting_permissions_modifier.cc
+++ b/chrome/browser/extensions/scripting_permissions_modifier.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
+#include "base/bind_helpers.h"
#include "base/feature_list.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "content/public/common/url_constants.h"
@@ -274,7 +275,8 @@
.GrantRuntimePermissions(
*extension_,
PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- explicit_hosts, scriptable_hosts));
+ explicit_hosts, scriptable_hosts),
+ base::DoNothing::Once());
}
bool ScriptingPermissionsModifier::HasGrantedHostPermission(
@@ -310,7 +312,8 @@
.RevokeRuntimePermissions(
*extension_,
PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- explicit_hosts, scriptable_hosts));
+ explicit_hosts, scriptable_hosts),
+ base::DoNothing::Once());
}
void ScriptingPermissionsModifier::RemoveAllGrantedHostPermissions() {
@@ -393,12 +396,14 @@
withheld.explicit_hosts(),
withheld.scriptable_hosts());
PermissionsUpdater(browser_context_)
- .GrantRuntimePermissions(*extension_, permissions);
+ .GrantRuntimePermissions(*extension_, permissions,
+ base::DoNothing::Once());
}
void ScriptingPermissionsModifier::WithholdHostPermissions() {
PermissionsUpdater(browser_context_)
- .RevokeRuntimePermissions(*extension_, *GetRevokablePermissions());
+ .RevokeRuntimePermissions(*extension_, *GetRevokablePermissions(),
+ base::DoNothing::Once());
}
} // namespace extensions
diff --git a/chrome/browser/extensions/scripting_permissions_modifier_unittest.cc b/chrome/browser/extensions/scripting_permissions_modifier_unittest.cc
index 61d4195d..d6c577e 100644
--- a/chrome/browser/extensions/scripting_permissions_modifier_unittest.cc
+++ b/chrome/browser/extensions/scripting_permissions_modifier_unittest.cc
@@ -10,6 +10,7 @@
#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/permissions_test_util.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/test/base/testing_profile.h"
@@ -486,9 +487,10 @@
URLPatternSet patterns;
patterns.AddPattern(URLPattern(Extension::kValidHostPermissionSchemes,
"https://example.com/*"));
- PermissionsUpdater(profile()).GrantOptionalPermissions(
- *extension, PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
- patterns, URLPatternSet()));
+ permissions_test_util::GrantOptionalPermissionsAndWaitForCompletion(
+ profile(), *extension,
+ PermissionSet(APIPermissionSet(), ManifestPermissionSet(), patterns,
+ URLPatternSet()));
}
EXPECT_THAT(GetEffectivePatternsAsStrings(*extension),
@@ -806,8 +808,8 @@
const URLPattern all_com_pattern(Extension::kValidHostPermissionSchemes,
"https://*.com/*");
- PermissionsUpdater(profile()).GrantRuntimePermissions(
- *extension,
+ permissions_test_util::GrantRuntimePermissionsAndWaitForCompletion(
+ profile(), *extension,
PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
URLPatternSet({all_com_pattern}), URLPatternSet()));
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 188dfd74..1f6a8de 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -303,6 +303,8 @@
"../browser/extensions/browsertest_util.h",
"../browser/extensions/extension_browsertest.cc",
"../browser/extensions/extension_browsertest.h",
+ "../browser/extensions/permissions_test_util.cc",
+ "../browser/extensions/permissions_test_util.h",
]
public_deps += [
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index 38d9896..97856e6 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -4,7 +4,12 @@
#include "extensions/browser/renderer_startup_helper.h"
+#include <utility>
+#include <vector>
+
+#include "base/bind_helpers.h"
#include "base/debug/dump_without_crashing.h"
+#include "base/feature_list.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
@@ -18,13 +23,16 @@
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#include "extensions/common/cors_util.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/features/feature_session_type.h"
#include "extensions/common/permissions/permissions_data.h"
+#include "services/network/public/cpp/features.h"
#include "ui/base/webui/web_ui_util.h"
+#include "url/origin.h"
using content::BrowserContext;
@@ -231,6 +239,20 @@
if (extension.is_theme())
return;
+ // Registers the initial origin access lists to the BrowserContext
+ // asynchronously.
+ url::Origin extension_origin = url::Origin::Create(extension.url());
+ std::vector<network::mojom::CorsOriginPatternPtr> allow_list =
+ CreateCorsOriginAccessAllowList(extension);
+ if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+ ExtensionsClient::Get()->AddOriginAccessPermissions(extension, true,
+ &allow_list);
+ }
+
+ content::BrowserContext::SetCorsOriginAccessListsForOrigin(
+ browser_context_, extension_origin, std::move(allow_list),
+ CreateCorsOriginAccessBlockList(extension), base::DoNothing::Once());
+
// We don't need to include tab permisisons here, since the extension
// was just loaded.
// Uninitialized renderers will be informed of the extension load during the
@@ -260,6 +282,14 @@
process->Send(new ExtensionMsg_Unloaded(extension.id()));
}
+ // Resets registered origin access lists in the BrowserContext asynchronously.
+ url::Origin extension_origin = url::Origin::Create(extension.url());
+ content::BrowserContext::SetCorsOriginAccessListsForOrigin(
+ browser_context_, extension_origin,
+ std::vector<network::mojom::CorsOriginPatternPtr>(),
+ std::vector<network::mojom::CorsOriginPatternPtr>(),
+ base::DoNothing::Once());
+
for (auto& process_extensions_pair : pending_active_extensions_)
process_extensions_pair.second.erase(extension.id());
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index dbb2086..0d99d6fc 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -27,7 +27,6 @@
#include "build/build_config.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
-#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/v8_value_converter.h"