[Extensions] Sync the 'allowed scripting on all urls' preference

Sync the flag for whether or not an extension is allowed to script on all urls,
independent of the flag to enable the feature. This (eventually) allows people
to install extensions on a machine with the flag without the scary warning
about what will happen if it's synced.

BUG=452756

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

Cr-Commit-Position: refs/heads/master@{#316100}
diff --git a/chrome/browser/apps/ephemeral_app_browsertest.cc b/chrome/browser/apps/ephemeral_app_browsertest.cc
index d28dd32..da8e8bf1 100644
--- a/chrome/browser/apps/ephemeral_app_browsertest.cc
+++ b/chrome/browser/apps/ephemeral_app_browsertest.cc
@@ -490,6 +490,7 @@
                               enable_from_sync,
                               false /* incognito enabled */,
                               false /* remote install */,
+                              extensions::ExtensionSyncData::BOOLEAN_UNSET,
                               kAppLaunchOrdinal,
                               kPageOrdinal,
                               extensions::LAUNCH_TYPE_REGULAR);
diff --git a/chrome/browser/extensions/active_script_controller_unittest.cc b/chrome/browser/extensions/active_script_controller_unittest.cc
index 3e416cd..3f191ade 100644
--- a/chrome/browser/extensions/active_script_controller_unittest.cc
+++ b/chrome/browser/extensions/active_script_controller_unittest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/extensions/active_script_controller.h"
 #include "chrome/browser/extensions/active_tab_permission_granter.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/extensions/extension_sync_service_factory.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/permissions_updater.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -31,6 +32,11 @@
 
 const char kAllHostsPermission[] = "*://*/*";
 
+// We skip syncing for testing purposes.
+KeyedService* BuildSyncService(content::BrowserContext* context) {
+  return nullptr;
+}
+
 }  // namespace
 
 // Unittests for the ActiveScriptController mostly test the internal logic
@@ -163,6 +169,9 @@
 void ActiveScriptControllerUnitTest::SetUp() {
   ChromeRenderViewHostTestHarness::SetUp();
 
+  ExtensionSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), &BuildSyncService);
+
   TabHelper::CreateForWebContents(web_contents());
   TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
   // These should never be NULL.
diff --git a/chrome/browser/extensions/app_sync_data.cc b/chrome/browser/extensions/app_sync_data.cc
index 6ee99da..6548ef5 100644
--- a/chrome/browser/extensions/app_sync_data.cc
+++ b/chrome/browser/extensions/app_sync_data.cc
@@ -29,13 +29,15 @@
                          bool enabled,
                          bool incognito_enabled,
                          bool remote_install,
+                         ExtensionSyncData::OptionalBoolean all_urls_enabled,
                          const syncer::StringOrdinal& app_launch_ordinal,
                          const syncer::StringOrdinal& page_ordinal,
                          extensions::LaunchType launch_type)
     : extension_sync_data_(extension,
                            enabled,
                            incognito_enabled,
-                           remote_install),
+                           remote_install,
+                           all_urls_enabled),
       app_launch_ordinal_(app_launch_ordinal),
       page_ordinal_(page_ordinal),
       launch_type_(launch_type) {
diff --git a/chrome/browser/extensions/app_sync_data.h b/chrome/browser/extensions/app_sync_data.h
index 82f49ea8..bdbb1641 100644
--- a/chrome/browser/extensions/app_sync_data.h
+++ b/chrome/browser/extensions/app_sync_data.h
@@ -34,6 +34,7 @@
               bool enabled,
               bool incognito_enabled,
               bool remote_install,
+              ExtensionSyncData::OptionalBoolean all_urls_enabled,
               const syncer::StringOrdinal& app_launch_ordinal,
               const syncer::StringOrdinal& page_ordinal,
               extensions::LaunchType launch_type);
diff --git a/chrome/browser/extensions/app_sync_data_unittest.cc b/chrome/browser/extensions/app_sync_data_unittest.cc
index e4c19ee..97fafbd 100644
--- a/chrome/browser/extensions/app_sync_data_unittest.cc
+++ b/chrome/browser/extensions/app_sync_data_unittest.cc
@@ -29,6 +29,7 @@
     extension_specifics->set_enabled(false);
     extension_specifics->set_incognito_enabled(true);
     extension_specifics->set_remote_install(false);
+    extension_specifics->set_all_urls_enabled(true);
     extension_specifics->set_installed_by_custodian(false);
     extension_specifics->set_name(kName);
   }
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 679c7eb..0e9e757 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -167,6 +167,7 @@
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
 using extensions::ExtensionResource;
+using extensions::ExtensionSyncData;
 using extensions::ExtensionSystem;
 using extensions::FakeSafeBrowsingDatabaseManager;
 using extensions::FeatureSwitch;
@@ -5861,7 +5862,7 @@
   ASSERT_TRUE(extension);
   ASSERT_TRUE(service()->IsExtensionEnabled(good0));
   extensions::ExtensionSyncData disable_good_crx(
-      *extension, false, false, false);
+      *extension, false, false, false, ExtensionSyncData::BOOLEAN_UNSET);
 
   // Then sync data arrives telling us to disable |good0|.
   syncer::SyncDataList sync_data;
@@ -5908,7 +5909,7 @@
   // Now sync data comes in that says to disable good0. This should be
   // ignored.
   extensions::ExtensionSyncData disable_good_crx(
-      *extension, false, false, false);
+      *extension, false, false, false, ExtensionSyncData::BOOLEAN_FALSE);
   syncer::SyncDataList sync_data;
   sync_data.push_back(disable_good_crx.GetSyncData());
   extension_sync_service()->MergeDataAndStartSyncing(
@@ -5946,6 +5947,7 @@
   EXPECT_EQ(service()->IsExtensionEnabled(good_crx), data.enabled());
   EXPECT_EQ(extensions::util::IsIncognitoEnabled(good_crx, profile()),
             data.incognito_enabled());
+  EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data.all_urls_enabled());
   EXPECT_TRUE(data.version().Equals(*extension->version()));
   EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension),
             data.update_url());
@@ -5977,6 +5979,7 @@
   EXPECT_EQ(service()->IsExtensionEnabled(good_crx), data.enabled());
   EXPECT_EQ(extensions::util::IsIncognitoEnabled(good_crx, profile()),
             data.incognito_enabled());
+  EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data.all_urls_enabled());
   EXPECT_TRUE(data.version().Equals(*extension->version()));
   EXPECT_EQ(extensions::ManifestURL::GetUpdateURL(extension),
             data.update_url());
@@ -6025,6 +6028,7 @@
     extensions::ExtensionSyncData data(list[0]);
     EXPECT_TRUE(data.enabled());
     EXPECT_FALSE(data.incognito_enabled());
+    EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data.all_urls_enabled());
   }
 
   service()->DisableExtension(good_crx, Extension::DISABLE_USER_ACTION);
@@ -6035,9 +6039,12 @@
     extensions::ExtensionSyncData data(list[0]);
     EXPECT_FALSE(data.enabled());
     EXPECT_FALSE(data.incognito_enabled());
+    EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET, data.all_urls_enabled());
   }
 
   extensions::util::SetIsIncognitoEnabled(good_crx, profile(), true);
+  extensions::util::SetAllowedScriptingOnAllUrls(
+      good_crx, profile(), false);
   {
     syncer::SyncDataList list =
         extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS);
@@ -6045,9 +6052,12 @@
     extensions::ExtensionSyncData data(list[0]);
     EXPECT_FALSE(data.enabled());
     EXPECT_TRUE(data.incognito_enabled());
+    EXPECT_EQ(ExtensionSyncData::BOOLEAN_FALSE, data.all_urls_enabled());
   }
 
   service()->EnableExtension(good_crx);
+  extensions::util::SetAllowedScriptingOnAllUrls(
+      good_crx, profile(), true);
   {
     syncer::SyncDataList list =
         extension_sync_service()->GetAllSyncData(syncer::EXTENSIONS);
@@ -6055,6 +6065,7 @@
     extensions::ExtensionSyncData data(list[0]);
     EXPECT_TRUE(data.enabled());
     EXPECT_TRUE(data.incognito_enabled());
+    EXPECT_EQ(ExtensionSyncData::BOOLEAN_TRUE, data.all_urls_enabled());
   }
 }
 
@@ -6338,6 +6349,12 @@
   InstallCRX(data_dir().AppendASCII("good.crx"), INSTALL_NEW);
   EXPECT_TRUE(service()->IsExtensionEnabled(good_crx));
   EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile()));
+  EXPECT_FALSE(extensions::util::HasSetAllowedScriptingOnAllUrls(
+      good_crx, profile()));
+  const bool kDefaultAllowedScripting =
+      extensions::util::DefaultAllowedScriptingOnAllUrls();
+  EXPECT_EQ(kDefaultAllowedScripting,
+            extensions::util::AllowedScriptingOnAllUrls(good_crx, profile()));
 
   sync_pb::EntitySpecifics specifics;
   sync_pb::ExtensionSpecifics* ext_specifics = specifics.mutable_extension();
@@ -6357,6 +6374,10 @@
     extension_sync_service()->ProcessSyncChanges(FROM_HERE, list);
     EXPECT_FALSE(service()->IsExtensionEnabled(good_crx));
     EXPECT_FALSE(extensions::util::IsIncognitoEnabled(good_crx, profile()));
+    EXPECT_FALSE(extensions::util::HasSetAllowedScriptingOnAllUrls(
+        good_crx, profile()));
+    EXPECT_EQ(kDefaultAllowedScripting,
+              extensions::util::AllowedScriptingOnAllUrls(good_crx, profile()));
   }
 
   {
@@ -6389,6 +6410,41 @@
     EXPECT_TRUE(extensions::util::IsIncognitoEnabled(good_crx, profile()));
   }
 
+  {
+    ext_specifics->set_enabled(true);
+    ext_specifics->set_all_urls_enabled(!kDefaultAllowedScripting);
+    syncer::SyncData sync_data =
+        syncer::SyncData::CreateLocalData(good_crx, "Name", specifics);
+    syncer::SyncChange sync_change(FROM_HERE,
+                                   syncer::SyncChange::ACTION_UPDATE,
+                                   sync_data);
+    syncer::SyncChangeList list(1);
+    list[0] = sync_change;
+    extension_sync_service()->ProcessSyncChanges(FROM_HERE, list);
+    EXPECT_TRUE(service()->IsExtensionEnabled(good_crx));
+    EXPECT_TRUE(extensions::util::HasSetAllowedScriptingOnAllUrls(
+        good_crx, profile()));
+    EXPECT_EQ(!kDefaultAllowedScripting,
+              extensions::util::AllowedScriptingOnAllUrls(good_crx, profile()));
+  }
+
+  {
+    ext_specifics->set_all_urls_enabled(kDefaultAllowedScripting);
+    syncer::SyncData sync_data =
+        syncer::SyncData::CreateLocalData(good_crx, "Name", specifics);
+    syncer::SyncChange sync_change(FROM_HERE,
+                                   syncer::SyncChange::ACTION_UPDATE,
+                                   sync_data);
+    syncer::SyncChangeList list(1);
+    list[0] = sync_change;
+    extension_sync_service()->ProcessSyncChanges(FROM_HERE, list);
+    EXPECT_TRUE(service()->IsExtensionEnabled(good_crx));
+    EXPECT_TRUE(extensions::util::HasSetAllowedScriptingOnAllUrls(
+        good_crx, profile()));
+    EXPECT_EQ(kDefaultAllowedScripting,
+              extensions::util::AllowedScriptingOnAllUrls(good_crx, profile()));
+  }
+
   EXPECT_FALSE(service()->pending_extension_manager()->IsIdPending(good_crx));
 }
 
diff --git a/chrome/browser/extensions/extension_sync_data.cc b/chrome/browser/extensions/extension_sync_data.cc
index ee55d1a..007887d 100644
--- a/chrome/browser/extensions/extension_sync_data.cc
+++ b/chrome/browser/extensions/extension_sync_data.cc
@@ -21,6 +21,7 @@
       enabled_(false),
       incognito_enabled_(false),
       remote_install_(false),
+      all_urls_enabled_(BOOLEAN_UNSET),
       installed_by_custodian_(false) {
 }
 
@@ -29,6 +30,7 @@
       enabled_(false),
       incognito_enabled_(false),
       remote_install_(false),
+      all_urls_enabled_(BOOLEAN_UNSET),
       installed_by_custodian_(false) {
   PopulateFromSyncData(sync_data);
 }
@@ -39,6 +41,7 @@
       enabled_(false),
       incognito_enabled_(false),
       remote_install_(false),
+      all_urls_enabled_(BOOLEAN_UNSET),
       installed_by_custodian_(false) {
   PopulateFromSyncData(sync_change.sync_data());
 }
@@ -46,12 +49,14 @@
 ExtensionSyncData::ExtensionSyncData(const Extension& extension,
                                      bool enabled,
                                      bool incognito_enabled,
-                                     bool remote_install)
+                                     bool remote_install,
+                                     OptionalBoolean all_urls_enabled)
     : id_(extension.id()),
       uninstalled_(false),
       enabled_(enabled),
       incognito_enabled_(incognito_enabled),
       remote_install_(remote_install),
+      all_urls_enabled_(all_urls_enabled),
       installed_by_custodian_(extension.was_installed_by_custodian()),
       version_(extension.from_bookmark() ? base::Version("0")
                                          : *extension.version()),
@@ -82,6 +87,8 @@
   specifics->set_enabled(enabled_);
   specifics->set_incognito_enabled(incognito_enabled_);
   specifics->set_remote_install(remote_install_);
+  if (all_urls_enabled_ != BOOLEAN_UNSET)
+    specifics->set_all_urls_enabled(all_urls_enabled_ == BOOLEAN_TRUE);
   specifics->set_installed_by_custodian(installed_by_custodian_);
   specifics->set_name(name_);
 }
@@ -107,6 +114,14 @@
   version_ = specifics_version;
   enabled_ = specifics.enabled();
   incognito_enabled_ = specifics.incognito_enabled();
+  if (specifics.has_all_urls_enabled()) {
+    all_urls_enabled_ =
+        specifics.all_urls_enabled() ? BOOLEAN_TRUE : BOOLEAN_FALSE;
+  } else {
+    // Set this explicitly (even though it's the default) on the offchance
+    // that someone is re-using an ExtensionSyncData object.
+    all_urls_enabled_ = BOOLEAN_UNSET;
+  }
   remote_install_ = specifics.remote_install();
   installed_by_custodian_ = specifics.installed_by_custodian();
   name_ = specifics.name();
diff --git a/chrome/browser/extensions/extension_sync_data.h b/chrome/browser/extensions/extension_sync_data.h
index 1e924dd..9e67043 100644
--- a/chrome/browser/extensions/extension_sync_data.h
+++ b/chrome/browser/extensions/extension_sync_data.h
@@ -26,13 +26,20 @@
 // A class that encapsulates the synced properties of an Extension.
 class ExtensionSyncData {
  public:
+  enum OptionalBoolean {
+    BOOLEAN_UNSET,
+    BOOLEAN_TRUE,
+    BOOLEAN_FALSE
+  };
+
   ExtensionSyncData();
   explicit ExtensionSyncData(const syncer::SyncData& sync_data);
   explicit ExtensionSyncData(const syncer::SyncChange& sync_change);
   ExtensionSyncData(const Extension& extension,
                     bool enabled,
                     bool incognito_enabled,
-                    bool remote_install);
+                    bool remote_install,
+                    OptionalBoolean all_urls_enabled);
   ~ExtensionSyncData();
 
   // Retrieve sync data from this class.
@@ -58,6 +65,7 @@
   bool enabled() const { return enabled_; }
   bool incognito_enabled() const { return incognito_enabled_; }
   bool remote_install() const { return remote_install_; }
+  OptionalBoolean all_urls_enabled() const { return all_urls_enabled_; }
   bool installed_by_custodian() const { return installed_by_custodian_; }
 
   // Version-dependent properties (i.e., should be used only when the
@@ -76,6 +84,7 @@
   bool enabled_;
   bool incognito_enabled_;
   bool remote_install_;
+  OptionalBoolean all_urls_enabled_;
   bool installed_by_custodian_;
   Version version_;
   GURL update_url_;
diff --git a/chrome/browser/extensions/extension_sync_data_unittest.cc b/chrome/browser/extensions/extension_sync_data_unittest.cc
index 46b3549..bd4e9ee 100644
--- a/chrome/browser/extensions/extension_sync_data_unittest.cc
+++ b/chrome/browser/extensions/extension_sync_data_unittest.cc
@@ -12,6 +12,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+namespace extensions {
+
 namespace {
 
 const char kValidId[] = "abcdefghijklmnopabcdefghijklmnop";
@@ -20,59 +22,115 @@
     "https://clients2.google.com/service/update2/crx";
 const char kName[] = "MyExtension";
 
+// Serializes a protobuf structure (entity specifics) into an ExtensionSyncData
+// and back again, and confirms that the input is the same as the output.
+void ProtobufToSyncDataEqual(const sync_pb::EntitySpecifics& entity) {
+  syncer::SyncData sync_data =
+      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
+  ExtensionSyncData extension_sync_data(sync_data);
+  syncer::SyncData output_sync_data = extension_sync_data.GetSyncData();
+  const sync_pb::ExtensionSpecifics& output =
+      output_sync_data.GetSpecifics().extension();
+  const sync_pb::ExtensionSpecifics& input = entity.extension();
+
+  // Check for field-by-field quality. It'd be nice if we could use
+  // AssertionResults here (instead of EXPECT_EQ) so that we could get valid
+  // line numbers, but it's not worth the ugliness of the verbose comparison.
+  EXPECT_EQ(input.id(), output.id());
+  EXPECT_EQ(input.name(), output.name());
+  EXPECT_EQ(input.version(), output.version());
+  EXPECT_EQ(input.update_url(), output.update_url());
+  EXPECT_EQ(input.enabled(), output.enabled());
+  EXPECT_EQ(input.incognito_enabled(), output.incognito_enabled());
+  EXPECT_EQ(input.remote_install(), output.remote_install());
+  EXPECT_EQ(input.installed_by_custodian(), output.installed_by_custodian());
+  EXPECT_EQ(input.has_all_urls_enabled(), output.has_all_urls_enabled());
+  if (input.has_all_urls_enabled())
+    EXPECT_EQ(input.all_urls_enabled(), output.all_urls_enabled());
+}
+
+// Serializes an ExtensionSyncData into a protobuf structure and back again, and
+// confirms that the input is the same as the output.
+void SyncDataToProtobufEqual(const ExtensionSyncData& input) {
+  syncer::SyncData sync_data = input.GetSyncData();
+  ExtensionSyncData output(sync_data);
+
+  EXPECT_EQ(input.id(), output.id());
+  EXPECT_EQ(input.uninstalled(), output.uninstalled());
+  EXPECT_EQ(input.enabled(), output.enabled());
+  EXPECT_EQ(input.incognito_enabled(), output.incognito_enabled());
+  EXPECT_EQ(input.remote_install(), output.remote_install());
+  EXPECT_EQ(input.installed_by_custodian(), output.installed_by_custodian());
+  EXPECT_EQ(input.all_urls_enabled(), output.all_urls_enabled());
+  EXPECT_TRUE(input.version().Equals(output.version()));
+  EXPECT_EQ(input.update_url(), output.update_url());
+  EXPECT_EQ(input.name(), output.name());
+}
+
+}  // namespace
+
 class ExtensionSyncDataTest : public testing::Test {
 };
 
-TEST_F(ExtensionSyncDataTest, SyncDataToExtensionSyncDataForExtension) {
+// Tests the conversion process from a protobuf to an ExtensionSyncData and vice
+// versa.
+TEST_F(ExtensionSyncDataTest, ExtensionSyncDataForExtension) {
   sync_pb::EntitySpecifics entity;
   sync_pb::ExtensionSpecifics* extension_specifics = entity.mutable_extension();
   extension_specifics->set_id(kValidId);
   extension_specifics->set_update_url(kValidUpdateUrl);
   extension_specifics->set_enabled(false);
   extension_specifics->set_incognito_enabled(true);
+  extension_specifics->set_remote_install(false);
+  extension_specifics->set_installed_by_custodian(false);
+  extension_specifics->set_all_urls_enabled(true);
   extension_specifics->set_version(kVersion);
   extension_specifics->set_name(kName);
-  syncer::SyncData sync_data =
-      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
 
-  extensions::ExtensionSyncData extension_sync_data(sync_data);
-  EXPECT_EQ(extension_specifics->id(), extension_sync_data.id());
-  EXPECT_EQ(extension_specifics->version(),
-            extension_sync_data.version().GetString());
-  EXPECT_EQ(extension_specifics->update_url(),
-            extension_sync_data.update_url().spec());
-  EXPECT_EQ(extension_specifics->enabled(), extension_sync_data.enabled());
-  EXPECT_EQ(extension_specifics->incognito_enabled(),
-            extension_sync_data.incognito_enabled());
-  EXPECT_EQ(extension_specifics->name(), extension_sync_data.name());
-  EXPECT_FALSE(extension_sync_data.uninstalled());
+  // Check the serialize-deserialize process for proto to ExtensionSyncData.
+  ProtobufToSyncDataEqual(entity);
+
+  // Explicitly test that conversion to an ExtensionSyncData gets the correct
+  // result (otherwise we just know that conversion to/from a proto gives us
+  // the same result, but don't know that it's right).
+  ExtensionSyncData extension_sync_data;
+  extension_sync_data.PopulateFromExtensionSpecifics(*extension_specifics);
+  EXPECT_EQ(kValidId, extension_sync_data.id());
+  EXPECT_EQ(GURL(kValidUpdateUrl), extension_sync_data.update_url());
+  EXPECT_EQ(false, extension_sync_data.enabled());
+  EXPECT_EQ(true, extension_sync_data.incognito_enabled());
+  EXPECT_EQ(false, extension_sync_data.remote_install());
+  EXPECT_EQ(ExtensionSyncData::BOOLEAN_TRUE,
+            extension_sync_data.all_urls_enabled());
+  EXPECT_TRUE(Version(kVersion).Equals(extension_sync_data.version()));
+  EXPECT_EQ(std::string(kName), extension_sync_data.name());
+
+  // Check the serialize-deserialize process for ExtensionSyncData to proto.
+  SyncDataToProtobufEqual(extension_sync_data);
+
+  // The most important thing to test is the "all urls" bit, since it is a
+  // tri-state boolean (and thus has more logic). Also flip another bit for a
+  // sanity check.
+  extension_specifics->set_all_urls_enabled(false);
+  extension_specifics->set_incognito_enabled(false);
+  ProtobufToSyncDataEqual(entity);
+
+  extension_sync_data.PopulateFromExtensionSpecifics(*extension_specifics);
+  EXPECT_EQ(ExtensionSyncData::BOOLEAN_FALSE,
+            extension_sync_data.all_urls_enabled());
+  EXPECT_EQ(false, extension_sync_data.incognito_enabled());
+
+  SyncDataToProtobufEqual(extension_sync_data);
+
+  extension_specifics->clear_all_urls_enabled();
+  ProtobufToSyncDataEqual(entity);
+
+  extension_sync_data.PopulateFromExtensionSpecifics(*extension_specifics);
+  EXPECT_FALSE(extension_specifics->has_all_urls_enabled());
+  EXPECT_EQ(ExtensionSyncData::BOOLEAN_UNSET,
+            extension_sync_data.all_urls_enabled());
+
+  SyncDataToProtobufEqual(extension_sync_data);
 }
 
-TEST_F(ExtensionSyncDataTest, ExtensionSyncDataToSyncDataForExtension) {
-  sync_pb::EntitySpecifics entity;
-  sync_pb::ExtensionSpecifics* input_extension = entity.mutable_extension();
-  input_extension->set_id(kValidId);
-  input_extension->set_update_url(kValidUpdateUrl);
-  input_extension->set_enabled(true);
-  input_extension->set_incognito_enabled(false);
-  input_extension->set_version(kVersion);
-  input_extension->set_name(kName);
-  syncer::SyncData sync_data =
-      syncer::SyncData::CreateLocalData("sync_tag", "non_unique_title", entity);
-  extensions::ExtensionSyncData extension_sync_data(sync_data);
-
-  syncer::SyncData output_sync_data = extension_sync_data.GetSyncData();
-  const sync_pb::ExtensionSpecifics& output_specifics =
-      output_sync_data.GetSpecifics().extension();
-  EXPECT_EQ(extension_sync_data.id(), output_specifics.id());
-  EXPECT_EQ(extension_sync_data.update_url().spec(),
-            output_specifics.update_url());
-  EXPECT_EQ(extension_sync_data.enabled(), output_specifics.enabled());
-  EXPECT_EQ(extension_sync_data.incognito_enabled(),
-            output_specifics.incognito_enabled());
-  EXPECT_EQ(extension_sync_data.version().GetString(),
-            output_specifics.version());
-  EXPECT_EQ(extension_sync_data.name(), output_specifics.name());
-}
-
-}  // namespace
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index ccb04ecf..da191a6 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -42,6 +42,7 @@
 using extensions::Extension;
 using extensions::ExtensionPrefs;
 using extensions::ExtensionRegistry;
+using extensions::ExtensionSyncData;
 using extensions::FeatureSwitch;
 
 namespace {
@@ -60,6 +61,25 @@
   CreateOrUpdateBookmarkApp(extension_service.get(), &synced_info);
 }
 
+// Returns the pref value for "all urls enabled" for the given extension id.
+ExtensionSyncData::OptionalBoolean GetAllowedOnAllUrlsOptionalBoolean(
+    const std::string& extension_id,
+    content::BrowserContext* context) {
+  bool allowed_on_all_urls =
+      extensions::util::AllowedScriptingOnAllUrls(extension_id, context);
+  // If the extension is not allowed on all urls (which is not the default),
+  // then we have to sync the preference.
+  if (!allowed_on_all_urls)
+    return ExtensionSyncData::BOOLEAN_FALSE;
+
+  // If the user has explicitly set a value, then we sync it.
+  if (extensions::util::HasSetAllowedScriptingOnAllUrls(extension_id, context))
+    return ExtensionSyncData::BOOLEAN_TRUE;
+
+  // Otherwise, unset.
+  return ExtensionSyncData::BOOLEAN_UNSET;
+}
+
 }  // namespace
 
 ExtensionSyncService::ExtensionSyncService(Profile* profile,
@@ -240,12 +260,10 @@
       i != change_list.end();
       ++i) {
     syncer::ModelType type = i->sync_data().GetDataType();
-    if (type == syncer::EXTENSIONS) {
-      extension_sync_bundle_.ProcessSyncChange(
-          extensions::ExtensionSyncData(*i));
-    } else if (type == syncer::APPS) {
+    if (type == syncer::EXTENSIONS)
+      extension_sync_bundle_.ProcessSyncChange(ExtensionSyncData(*i));
+    else if (type == syncer::APPS)
       app_sync_bundle_.ProcessSyncChange(extensions::AppSyncData(*i));
-    }
   }
 
   extension_prefs_->app_sorting()->FixNTPOrdinalCollisions();
@@ -253,14 +271,15 @@
   return syncer::SyncError();
 }
 
-extensions::ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
+ExtensionSyncData ExtensionSyncService::GetExtensionSyncData(
     const Extension& extension) const {
-  return extensions::ExtensionSyncData(
+  return ExtensionSyncData(
       extension,
       extension_service_->IsExtensionEnabled(extension.id()),
       extensions::util::IsIncognitoEnabled(extension.id(), profile_),
       extension_prefs_->HasDisableReason(extension.id(),
-                                         Extension::DISABLE_REMOTE_INSTALL));
+                                         Extension::DISABLE_REMOTE_INSTALL),
+      GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_));
 }
 
 extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
@@ -271,15 +290,16 @@
       extensions::util::IsIncognitoEnabled(extension.id(), profile_),
       extension_prefs_->HasDisableReason(extension.id(),
                                          Extension::DISABLE_REMOTE_INSTALL),
+      GetAllowedOnAllUrlsOptionalBoolean(extension.id(), profile_),
       extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
       extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
       extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
 }
 
-std::vector<extensions::ExtensionSyncData>
-  ExtensionSyncService::GetExtensionSyncDataList() const {
+std::vector<ExtensionSyncData>
+    ExtensionSyncService::GetExtensionSyncDataList() const {
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
-  std::vector<extensions::ExtensionSyncData> extension_sync_list;
+  std::vector<ExtensionSyncData> extension_sync_list;
   extension_sync_bundle_.GetExtensionSyncDataListHelper(
       registry->enabled_extensions(), &extension_sync_list);
   extension_sync_bundle_.GetExtensionSyncDataListHelper(
@@ -287,7 +307,7 @@
   extension_sync_bundle_.GetExtensionSyncDataListHelper(
       registry->terminated_extensions(), &extension_sync_list);
 
-  std::vector<extensions::ExtensionSyncData> pending_extensions =
+  std::vector<ExtensionSyncData> pending_extensions =
       extension_sync_bundle_.GetPendingData();
   extension_sync_list.insert(extension_sync_list.begin(),
                              pending_extensions.begin(),
@@ -317,7 +337,7 @@
 }
 
 bool ExtensionSyncService::ProcessExtensionSyncData(
-    const extensions::ExtensionSyncData& extension_sync_data) {
+    const ExtensionSyncData& extension_sync_data) {
   if (!ProcessExtensionSyncDataHelper(extension_sync_data,
                                       syncer::EXTENSIONS)) {
     extension_sync_bundle_.AddPendingExtension(extension_sync_data.id(),
@@ -443,7 +463,7 @@
 }
 
 bool ExtensionSyncService::ProcessExtensionSyncDataHelper(
-    const extensions::ExtensionSyncData& extension_sync_data,
+    const ExtensionSyncData& extension_sync_data,
     syncer::ModelType type) {
   const std::string& id = extension_sync_data.id();
   const Extension* extension = extension_service_->GetInstalledExtension(id);
@@ -512,6 +532,14 @@
       id, profile_, extension_sync_data.incognito_enabled());
   extension = NULL;  // No longer safe to use.
 
+  // Update the all urls flag.
+  if (extension_sync_data.all_urls_enabled() !=
+          ExtensionSyncData::BOOLEAN_UNSET) {
+    bool allowed = extension_sync_data.all_urls_enabled() ==
+        ExtensionSyncData::BOOLEAN_TRUE;
+    extensions::util::SetAllowedScriptingOnAllUrls(id, profile_, allowed);
+  }
+
   if (extension_installed) {
     // If the extension is already installed, check if it's outdated.
     if (version_compare_result < 0) {
diff --git a/chrome/browser/extensions/extension_sync_service.h b/chrome/browser/extensions/extension_sync_service.h
index 9efb52d..cd180e45 100644
--- a/chrome/browser/extensions/extension_sync_service.h
+++ b/chrome/browser/extensions/extension_sync_service.h
@@ -11,6 +11,7 @@
 #include "base/compiler_specific.h"
 #include "chrome/browser/extensions/app_sync_bundle.h"
 #include "chrome/browser/extensions/extension_sync_bundle.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
 #include "chrome/browser/extensions/pending_enables.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "extensions/browser/extension_prefs.h"
@@ -19,7 +20,6 @@
 #include "sync/api/sync_change.h"
 #include "sync/api/syncable_service.h"
 
-class ExtensionSyncData;
 class Profile;
 
 namespace base {
@@ -28,8 +28,6 @@
 
 namespace extensions {
 class AppSyncData;
-class ExtensionPrefs;
-class ExtensionSyncData;
 }  // namespace extensions
 
 namespace syncer {
diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc
index 8cf30d4..29479c49f 100644
--- a/chrome/browser/extensions/extension_util.cc
+++ b/chrome/browser/extensions/extension_util.cc
@@ -99,6 +99,11 @@
         updater.GrantWithheldImpliedAllHosts(extension);
       else
         updater.WithholdImpliedAllHosts(extension);
+
+      // If this was an update to permissions, we also need to sync the change.
+      ExtensionSyncService* sync_service = ExtensionSyncService::Get(context);
+      if (sync_service)  // sync_service can be null in unittests.
+        sync_service->SyncExtensionChangeIfNeeded(*extension);
     }
   }
 }
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index cbdac9c0..19f360d 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -411,6 +411,7 @@
       original_data.extension_sync_data().enabled(),
       original_data.extension_sync_data().incognito_enabled(),
       original_data.extension_sync_data().remote_install(),
+      original_data.extension_sync_data().all_urls_enabled(),
       original_data.app_launch_ordinal(),
       original_data.page_ordinal(),
       extensions::NUM_LAUNCH_TYPES);