Add a withheld permissions model to PermissionsData. Withheld permissions are the permissions which were requested by the extension, but not granted due to how dangerous/powerful they are. Currently, these withheld permissions are only used for all hosts, and only behind the scripts_require_action flag.
BUG=362353
Review URL: https://codereview.chromium.org/348313003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281605 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/active_script_controller.cc b/chrome/browser/extensions/active_script_controller.cc
index fab44a0..1b35b28 100644
--- a/chrome/browser/extensions/active_script_controller.cc
+++ b/chrome/browser/extensions/active_script_controller.cc
@@ -25,11 +25,32 @@
#include "extensions/common/extension_messages.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/feature_switch.h"
+#include "extensions/common/manifest.h"
#include "extensions/common/permissions/permissions_data.h"
#include "ipc/ipc_message_macros.h"
namespace extensions {
+namespace {
+
+// Returns true if the extension should be regarded as a "permitted" extension
+// for the case of metrics. We need this because we only actually withhold
+// permissions if the switch is enabled, but want to record metrics in all
+// cases.
+// "ExtensionWouldHaveHadHostPermissionsWithheldIfSwitchWasOn()" would be
+// more accurate, but too long.
+bool ShouldRecordExtension(const Extension* extension) {
+ return extension->ShouldDisplayInExtensionSettings() &&
+ !Manifest::IsPolicyLocation(extension->location()) &&
+ !Manifest::IsComponentLocation(extension->location()) &&
+ !PermissionsData::CanExecuteScriptEverywhere(extension) &&
+ extension->permissions_data()
+ ->active_permissions()
+ ->ShouldWarnAllHosts();
+}
+
+} // namespace
+
ActiveScriptController::ActiveScriptController(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
@@ -56,39 +77,6 @@
return location_bar_controller->active_script_controller();
}
-bool ActiveScriptController::RequiresUserConsentForScriptInjection(
- const Extension* extension) {
- CHECK(extension);
- if (!extension->permissions_data()->RequiresActionForScriptExecution(
- extension,
- SessionID::IdForTab(web_contents()),
- web_contents()->GetVisibleURL()) ||
- util::AllowedScriptingOnAllUrls(extension->id(),
- web_contents()->GetBrowserContext())) {
- return false;
- }
-
- // If the feature is not enabled, we automatically allow all extensions to
- // run scripts.
- if (!enabled_)
- permitted_extensions_.insert(extension->id());
-
- return permitted_extensions_.count(extension->id()) == 0;
-}
-
-void ActiveScriptController::RequestScriptInjection(
- const Extension* extension,
- const base::Closure& callback) {
- CHECK(extension);
- PendingRequestList& list = pending_requests_[extension->id()];
- list.push_back(callback);
-
- // If this was the first entry, notify the location bar that there's a new
- // icon.
- if (list.size() == 1u)
- LocationBarController::NotifyChange(web_contents());
-}
-
void ActiveScriptController::OnActiveTabPermissionGranted(
const Extension* extension) {
RunPendingForExtension(extension);
@@ -159,13 +147,52 @@
pending_requests_.erase(iter);
}
+PermissionsData::AccessType
+ActiveScriptController::RequiresUserConsentForScriptInjection(
+ const Extension* extension,
+ UserScript::InjectionType type) {
+ CHECK(extension);
+
+ // If the feature is not enabled, we automatically allow all extensions to
+ // run scripts.
+ if (!enabled_)
+ permitted_extensions_.insert(extension->id());
+
+ // Allow the extension if it's been explicitly granted permission.
+ if (permitted_extensions_.count(extension->id()) > 0)
+ return PermissionsData::ACCESS_ALLOWED;
+
+ GURL url = web_contents()->GetVisibleURL();
+ int tab_id = SessionID::IdForTab(web_contents());
+ switch (type) {
+ case UserScript::CONTENT_SCRIPT:
+ return extension->permissions_data()->GetContentScriptAccess(
+ extension, url, url, tab_id, -1, NULL);
+ case UserScript::PROGRAMMATIC_SCRIPT:
+ return extension->permissions_data()->GetPageAccess(
+ extension, url, url, tab_id, -1, NULL);
+ }
+
+ NOTREACHED();
+ return PermissionsData::ACCESS_DENIED;
+}
+
+void ActiveScriptController::RequestScriptInjection(
+ const Extension* extension,
+ const base::Closure& callback) {
+ CHECK(extension);
+ PendingRequestList& list = pending_requests_[extension->id()];
+ list.push_back(callback);
+
+ // If this was the first entry, notify the location bar that there's a new
+ // icon.
+ if (list.size() == 1u)
+ LocationBarController::NotifyChange(web_contents());
+}
+
void ActiveScriptController::RunPendingForExtension(
const Extension* extension) {
DCHECK(extension);
- PendingRequestMap::iterator iter =
- pending_requests_.find(extension->id());
- if (iter == pending_requests_.end())
- return;
content::NavigationEntry* visible_entry =
web_contents()->GetController().GetVisibleEntry();
@@ -178,6 +205,11 @@
// *before* running them to guard against the crazy case where running the
// callbacks adds more entries.
permitted_extensions_.insert(extension->id());
+
+ PendingRequestMap::iterator iter = pending_requests_.find(extension->id());
+ if (iter == pending_requests_.end())
+ return;
+
PendingRequestList requests;
iter->second.swap(requests);
pending_requests_.erase(extension->id());
@@ -202,6 +234,7 @@
void ActiveScriptController::OnRequestScriptInjectionPermission(
const std::string& extension_id,
+ UserScript::InjectionType script_type,
int64 request_id) {
if (!Extension::IdIsValid(extension_id)) {
NOTREACHED() << "'" << extension_id << "' is not a valid id.";
@@ -220,25 +253,37 @@
// ran (because this feature is not enabled). Add the extension to the list of
// permitted extensions (for metrics), and return immediately.
if (request_id == -1) {
- DCHECK(!enabled_);
- permitted_extensions_.insert(extension->id());
+ if (ShouldRecordExtension(extension)) {
+ DCHECK(!enabled_);
+ permitted_extensions_.insert(extension->id());
+ }
return;
}
- if (RequiresUserConsentForScriptInjection(extension)) {
- // This base::Unretained() is safe, because the callback is only invoked by
- // this object.
- RequestScriptInjection(
- extension,
- base::Bind(&ActiveScriptController::PermitScriptInjection,
- base::Unretained(this),
- request_id));
- } else {
- PermitScriptInjection(request_id);
+ switch (RequiresUserConsentForScriptInjection(extension, script_type)) {
+ case PermissionsData::ACCESS_ALLOWED:
+ PermitScriptInjection(request_id);
+ break;
+ case PermissionsData::ACCESS_WITHHELD:
+ // This base::Unretained() is safe, because the callback is only invoked
+ // by this object.
+ RequestScriptInjection(
+ extension,
+ base::Bind(&ActiveScriptController::PermitScriptInjection,
+ base::Unretained(this),
+ request_id));
+ break;
+ case PermissionsData::ACCESS_DENIED:
+ // We should usually only get a "deny access" if the page changed (as the
+ // renderer wouldn't have requested permission if the answer was always
+ // "no"). Just let the request fizzle and die.
+ break;
}
}
void ActiveScriptController::PermitScriptInjection(int64 request_id) {
+ // This only sends the response to the renderer - the process of adding the
+ // extension to the list of |permitted_extensions_| is done elsewhere.
content::RenderViewHost* render_view_host =
web_contents()->GetRenderViewHost();
if (render_view_host) {