[Extensions] Refactor click-to-script permissioning

Click-to-script allows users to withhold all-hosts and all-hosts-like
permissions, but the code to do this is pretty staggered. Consolidate more of
it into a single class

Review URL: https://codereview.chromium.org/1397443011

Cr-Commit-Position: refs/heads/master@{#354271}
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index 7ad22f6..5957a3f 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -9,7 +9,7 @@
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
-#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/permissions.h"
 #include "content/public/browser/notification_observer.h"
@@ -36,27 +36,6 @@
 
 namespace {
 
-// Returns a set of single origin permissions from |permissions| that match
-// |bounds|. This is necessary for two reasons:
-//   a) single origin active permissions can get filtered out in
-//      GetBoundedActivePermissions because they are not recognized as a subset
-//      of all-host permissions
-//   b) active permissions that do not match any manifest permissions can
-//      exist if a manifest permission is dropped
-URLPatternSet FilterSingleOriginPermissions(const URLPatternSet& permissions,
-                                            const URLPatternSet& bounds) {
-  URLPatternSet single_origin_permissions;
-  for (URLPatternSet::const_iterator iter = permissions.begin();
-       iter != permissions.end();
-       ++iter) {
-    if (iter->MatchesSingleOrigin() &&
-        bounds.MatchesURL(GURL(iter->GetAsString()))) {
-      single_origin_permissions.AddPattern(*iter);
-    }
-  }
-  return single_origin_permissions;
-}
-
 // Returns a PermissionSet that has the active permissions of the extension,
 // bounded to its current manifest.
 scoped_ptr<const PermissionSet> GetBoundedActivePermissions(
@@ -92,23 +71,6 @@
   return adjusted_active;
 }
 
-// Divvy up the |url patterns| between those we grant and those we do not. If
-// |withhold_permissions| is false (because the requisite feature is not
-// enabled), no permissions are withheld.
-void SegregateUrlPermissions(const URLPatternSet& url_patterns,
-                             bool withhold_permissions,
-                             URLPatternSet* granted,
-                             URLPatternSet* withheld) {
-  for (URLPatternSet::const_iterator iter = url_patterns.begin();
-       iter != url_patterns.end();
-       ++iter) {
-    if (withhold_permissions && iter->ImpliesAllHosts())
-      withheld->AddPattern(*iter);
-    else
-      granted->AddPattern(*iter);
-  }
-}
-
 }  // namespace
 
 PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
@@ -131,7 +93,10 @@
   scoped_ptr<const PermissionSet> added =
       PermissionSet::CreateDifference(*total, active);
 
-  SetPermissions(extension, total.Pass(), nullptr);
+  scoped_ptr<const PermissionSet> new_withheld =
+      PermissionSet::CreateDifference(
+          extension->permissions_data()->withheld_permissions(), permissions);
+  SetPermissions(extension, total.Pass(), new_withheld.Pass());
 
   // Update the granted permissions so we don't auto-disable the extension.
   GrantActivePermissions(extension);
@@ -191,44 +156,16 @@
 
 scoped_ptr<const PermissionSet> PermissionsUpdater::GetRevokablePermissions(
     const Extension* extension) const {
-  // Optional permissions are revokable.
-  scoped_ptr<const PermissionSet> wrapper;
-  const PermissionSet* revokable_permissions =
-      &PermissionsParser::GetOptionalPermissions(extension);
-  const PermissionSet& active_permissions =
-      extension->permissions_data()->active_permissions();
-  // If click-to-script is enabled, then any hosts that are granted, but not
-  // listed explicitly as a required permission, are also revokable.
-  if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
-    const PermissionSet& required_permissions =
-        PermissionsParser::GetRequiredPermissions(extension);
-    auto find_revokable_hosts = [](const URLPatternSet& active_hosts,
-                                   const URLPatternSet& required_hosts) {
-      URLPatternSet revokable_hosts;
-      for (const URLPattern& pattern : active_hosts) {
-        if (std::find(required_hosts.begin(), required_hosts.end(), pattern) ==
-            required_hosts.end()) {
-          revokable_hosts.AddPattern(pattern);
-        }
-      }
-      return revokable_hosts;
-    };
-    URLPatternSet revokable_explicit_hosts =
-        find_revokable_hosts(active_permissions.explicit_hosts(),
-                             required_permissions.explicit_hosts());
-    URLPatternSet revokable_scriptable_hosts =
-        find_revokable_hosts(active_permissions.scriptable_hosts(),
-                             required_permissions.scriptable_hosts());
-    scoped_ptr<const PermissionSet> revokable_host_permissions(
-        new PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                          revokable_explicit_hosts,
-                          revokable_scriptable_hosts));
-    wrapper = PermissionSet::CreateUnion(*revokable_permissions,
-                                         *revokable_host_permissions);
-    revokable_permissions = wrapper.get();
-  }
-  return PermissionSet::CreateIntersection(active_permissions,
-                                           *revokable_permissions);
+  // The user can revoke any permissions they granted. In other words, any
+  // permissions the extension didn't start with can be revoked.
+  const PermissionSet& required =
+      PermissionsParser::GetRequiredPermissions(extension);
+  scoped_ptr<const PermissionSet> granted;
+  scoped_ptr<const PermissionSet> withheld;
+  ScriptingPermissionsModifier(browser_context_, make_scoped_refptr(extension))
+      .WithholdPermissions(required, &granted, &withheld, false);
+  return PermissionSet::CreateDifference(
+      extension->permissions_data()->active_permissions(), *granted);
 }
 
 void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {
@@ -240,142 +177,30 @@
 }
 
 void PermissionsUpdater::InitializePermissions(const Extension* extension) {
-  scoped_ptr<const PermissionSet> active_wrapper;
   scoped_ptr<const PermissionSet> bounded_wrapper;
-  const PermissionSet* active_permissions = nullptr;
   const PermissionSet* bounded_active = nullptr;
   // If |extension| is a transient dummy extension, we do not want to look for
   // it in preferences.
   if (init_flag_ & INIT_FLAG_TRANSIENT) {
-    active_permissions = bounded_active =
-        &extension->permissions_data()->active_permissions();
+    bounded_active = &extension->permissions_data()->active_permissions();
   } else {
-    // As part of initializing permissions, we restrict access to the main
-    // thread.
-    active_wrapper = ExtensionPrefs::Get(browser_context_)
-                         ->GetActivePermissions(extension->id());
-    active_permissions = active_wrapper.get();
+    scoped_ptr<const PermissionSet> active_permissions =
+        ExtensionPrefs::Get(browser_context_)
+            ->GetActivePermissions(extension->id());
     bounded_wrapper =
-        GetBoundedActivePermissions(extension, active_permissions);
+        GetBoundedActivePermissions(extension, active_permissions.get());
     bounded_active = bounded_wrapper.get();
   }
 
-  // Determine whether or not to withhold host permissions.
-  bool should_withhold_permissions = false;
-  if (PermissionsData::ScriptsMayRequireActionForExtension(extension,
-                                                           *bounded_active)) {
-    should_withhold_permissions =
-        init_flag_ & INIT_FLAG_TRANSIENT ?
-            !util::DefaultAllowedScriptingOnAllUrls() :
-            !util::AllowedScriptingOnAllUrls(extension->id(), browser_context_);
-  }
+  scoped_ptr<const PermissionSet> granted_permissions;
+  scoped_ptr<const PermissionSet> withheld_permissions;
+  ScriptingPermissionsModifier(browser_context_, make_scoped_refptr(extension))
+      .WithholdPermissions(*bounded_active, &granted_permissions,
+                           &withheld_permissions,
+                           (init_flag_ & INIT_FLAG_TRANSIENT) == 0);
 
-  URLPatternSet granted_explicit_hosts;
-  URLPatternSet withheld_explicit_hosts;
-  SegregateUrlPermissions(bounded_active->explicit_hosts(),
-                          should_withhold_permissions,
-                          &granted_explicit_hosts,
-                          &withheld_explicit_hosts);
-
-  URLPatternSet granted_scriptable_hosts;
-  URLPatternSet withheld_scriptable_hosts;
-  SegregateUrlPermissions(bounded_active->scriptable_hosts(),
-                          should_withhold_permissions,
-                          &granted_scriptable_hosts,
-                          &withheld_scriptable_hosts);
-
-  // After withholding permissions, add back any origins to the active set that
-  // may have been lost during the set operations that would have dropped them.
-  // For example, the union of <all_urls> and "example.com" is <all_urls>, so
-  // we may lose "example.com". However, "example.com" is important once
-  // <all_urls> is stripped during withholding.
-  if (active_permissions) {
-    granted_explicit_hosts.AddPatterns(
-        FilterSingleOriginPermissions(active_permissions->explicit_hosts(),
-                                      bounded_active->explicit_hosts()));
-    granted_scriptable_hosts.AddPatterns(
-        FilterSingleOriginPermissions(active_permissions->scriptable_hosts(),
-                                      bounded_active->scriptable_hosts()));
-  }
-
-  scoped_ptr<const PermissionSet> new_permissions(new PermissionSet(
-      bounded_active->apis(), bounded_active->manifest_permissions(),
-      granted_explicit_hosts, granted_scriptable_hosts));
-
-  scoped_ptr<const PermissionSet> withheld(
-      new PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
-                        withheld_explicit_hosts, withheld_scriptable_hosts));
-  SetPermissions(extension, new_permissions.Pass(), withheld.Pass());
-}
-
-void PermissionsUpdater::WithholdImpliedAllHosts(const Extension* extension) {
-  const PermissionSet& active =
-      extension->permissions_data()->active_permissions();
-  const PermissionSet& withheld =
-      extension->permissions_data()->withheld_permissions();
-
-  URLPatternSet withheld_scriptable = withheld.scriptable_hosts();
-  URLPatternSet active_scriptable;
-  SegregateUrlPermissions(active.scriptable_hosts(),
-                          true,  // withhold permissions
-                          &active_scriptable, &withheld_scriptable);
-
-  URLPatternSet withheld_explicit = withheld.explicit_hosts();
-  URLPatternSet active_explicit;
-  SegregateUrlPermissions(active.explicit_hosts(),
-                          true,  // withhold permissions
-                          &active_explicit, &withheld_explicit);
-
-  URLPatternSet delta_explicit =
-      URLPatternSet::CreateDifference(active.explicit_hosts(), active_explicit);
-  URLPatternSet delta_scriptable = URLPatternSet::CreateDifference(
-      active.scriptable_hosts(), active_scriptable);
-
-  SetPermissions(extension, make_scoped_ptr(new PermissionSet(
-                                active.apis(), active.manifest_permissions(),
-                                active_explicit, active_scriptable)),
-                 make_scoped_ptr(new PermissionSet(
-                     withheld.apis(), withheld.manifest_permissions(),
-                     withheld_explicit, withheld_scriptable)));
-
-  NotifyPermissionsUpdated(
-      REMOVED, extension,
-      PermissionSet(APIPermissionSet(), ManifestPermissionSet(), delta_explicit,
-                    delta_scriptable));
-}
-
-void PermissionsUpdater::GrantWithheldImpliedAllHosts(
-    const Extension* extension) {
-  const PermissionSet& active =
-      extension->permissions_data()->active_permissions();
-  const PermissionSet& withheld =
-      extension->permissions_data()->withheld_permissions();
-
-  // Move the all-hosts permission from withheld to active.
-  // We can cheat a bit here since we know that the only host permission we
-  // withhold is allhosts (or something similar enough to it), so we can just
-  // grant all withheld host permissions.
-  URLPatternSet explicit_hosts = URLPatternSet::CreateUnion(
-      active.explicit_hosts(), withheld.explicit_hosts());
-  URLPatternSet scriptable_hosts = URLPatternSet::CreateUnion(
-      active.scriptable_hosts(), withheld.scriptable_hosts());
-
-  URLPatternSet delta_explicit =
-      URLPatternSet::CreateDifference(explicit_hosts, active.explicit_hosts());
-  URLPatternSet delta_scriptable = URLPatternSet::CreateDifference(
-      scriptable_hosts, active.scriptable_hosts());
-
-  // Since we only withhold host permissions (so far), we know that withheld
-  // permissions will be empty.
-  SetPermissions(extension, make_scoped_ptr(new PermissionSet(
-                                active.apis(), active.manifest_permissions(),
-                                explicit_hosts, scriptable_hosts)),
-                 make_scoped_ptr(new PermissionSet()));
-
-  NotifyPermissionsUpdated(
-      ADDED, extension,
-      PermissionSet(APIPermissionSet(), ManifestPermissionSet(), delta_explicit,
-                    delta_scriptable));
+  SetPermissions(extension, granted_permissions.Pass(),
+                 withheld_permissions.Pass());
 }
 
 void PermissionsUpdater::SetPermissions(