Support remote installation of extensions and apps.

To enable this support, the following things are changed:
* ExtensionSyncData, and the ExtensionSpecifics protobuf have a new
  remote_install field, to mark a remotely installed extension.
* A new DISABLE_REMOTE_INSTALL reason is added to track this state
  locally.
* CrxInstaller::allow_silent_install is changed from a bool to a
  three-state enum, where the options are:
    - install after showing a permission prompt,
    - install and grant all permissions without a prompt and
    - install without a prompt and don't grant any permissions
* AddExtensionDisabledError is modified to keep track of the disable
  reasons, and change the text that is displayed accordingly.
* ExtensionInstallPrompt has a new prompt type with different text
  for remotely installed extensions.

BUG=365737

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270900 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/app_sync_data.cc b/chrome/browser/extensions/app_sync_data.cc
index 5b1623e..1ecbb24c 100644
--- a/chrome/browser/extensions/app_sync_data.cc
+++ b/chrome/browser/extensions/app_sync_data.cc
@@ -27,10 +27,14 @@
 AppSyncData::AppSyncData(const Extension& extension,
                          bool enabled,
                          bool incognito_enabled,
+                         bool remote_install,
                          const syncer::StringOrdinal& app_launch_ordinal,
                          const syncer::StringOrdinal& page_ordinal,
                          extensions::LaunchType launch_type)
-    : extension_sync_data_(extension, enabled, incognito_enabled),
+    : extension_sync_data_(extension,
+                           enabled,
+                           incognito_enabled,
+                           remote_install),
       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 d1164c7..b8499e7 100644
--- a/chrome/browser/extensions/app_sync_data.h
+++ b/chrome/browser/extensions/app_sync_data.h
@@ -32,6 +32,7 @@
   AppSyncData(const Extension& extension,
               bool enabled,
               bool incognito_enabled,
+              bool remote_install,
               const syncer::StringOrdinal& app_launch_ordinal,
               const syncer::StringOrdinal& page_ordinal,
               extensions::LaunchType launch_type);
@@ -63,11 +64,11 @@
 
   const std::string& bookmark_app_url() const {
     return bookmark_app_url_;
-  };
+  }
 
   const std::string& bookmark_app_description() const {
     return bookmark_app_description_;
-  };
+  }
 
  private:
   // Convert an AppSyncData back out to a sync structure.
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 14bd23f..7b5ff2d 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -105,10 +105,9 @@
   return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
 }
 
-CrxInstaller::CrxInstaller(
-    base::WeakPtr<ExtensionService> service_weak,
-    scoped_ptr<ExtensionInstallPrompt> client,
-    const WebstoreInstaller::Approval* approval)
+CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
+                           scoped_ptr<ExtensionInstallPrompt> client,
+                           const WebstoreInstaller::Approval* approval)
     : install_directory_(service_weak->install_directory()),
       install_source_(Manifest::INTERNAL),
       approved_(false),
@@ -123,6 +122,7 @@
       client_(client.release()),
       apps_require_extension_mime_type_(false),
       allow_silent_install_(false),
+      grant_permissions_(true),
       install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
       creation_flags_(Extension::NO_FLAGS),
       off_store_install_allow_reason_(OffStoreInstallDisallowed),
@@ -802,7 +802,7 @@
     // We update the extension's granted permissions if the user already
     // approved the install (client_ is non NULL), or we are allowed to install
     // this silently.
-    if (client_ || allow_silent_install_) {
+    if ((client_ || allow_silent_install_) && grant_permissions_) {
       PermissionsUpdater perms_updater(profile());
       perms_updater.GrantActivePermissions(extension());
     }
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 8e3575b..42700931 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -133,6 +133,9 @@
   bool allow_silent_install() const { return allow_silent_install_; }
   void set_allow_silent_install(bool val) { allow_silent_install_ = val; }
 
+  bool grant_permissions() const { return grant_permissions_; }
+  void set_grant_permissions(bool val) { grant_permissions_ = val; }
+
   bool is_gallery_install() const {
     return (creation_flags_ & Extension::FROM_WEBSTORE) > 0;
   }
@@ -344,10 +347,14 @@
   bool apps_require_extension_mime_type_;
 
   // Allows for the possibility of a normal install (one in which a |client|
-  // is provided in the ctor) to procede without showing the permissions prompt
+  // is provided in the ctor) to proceed without showing the permissions prompt
   // dialog.
   bool allow_silent_install_;
 
+  // Allows for the possibility of an installation without granting any
+  // permissions to the extension.
+  bool grant_permissions_;
+
   // The value of the content type header sent with the CRX.
   // Ignorred unless |require_extension_mime_type_| is true.
   std::string original_mime_type_;
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc
index ece2144a..09bfaf6 100644
--- a/chrome/browser/extensions/extension_disabled_ui.cc
+++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -142,6 +142,7 @@
  public:
   ExtensionDisabledGlobalError(ExtensionService* service,
                                const Extension* extension,
+                               bool is_remote_install,
                                const gfx::Image& icon);
   virtual ~ExtensionDisabledGlobalError();
 
@@ -172,6 +173,7 @@
  private:
   ExtensionService* service_;
   const Extension* extension_;
+  bool is_remote_install_;
   gfx::Image icon_;
 
   // How the user responded to the error; used for metrics.
@@ -195,9 +197,11 @@
 ExtensionDisabledGlobalError::ExtensionDisabledGlobalError(
     ExtensionService* service,
     const Extension* extension,
+    bool is_remote_install,
     const gfx::Image& icon)
     : service_(service),
       extension_(extension),
+      is_remote_install_(is_remote_install),
       icon_(icon),
       user_response_(IGNORED),
       menu_command_id_(GetMenuCommandID()) {
@@ -219,9 +223,15 @@
 
 ExtensionDisabledGlobalError::~ExtensionDisabledGlobalError() {
   ReleaseMenuCommandID(menu_command_id_);
-  UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse",
-                            user_response_,
-                            EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
+  if (is_remote_install_) {
+    UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponseRemoteInstall",
+                              user_response_,
+                              EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Extensions.DisabledUIUserResponse",
+                              user_response_,
+                              EXTENSION_DISABLED_UI_BUCKET_BOUNDARY);
+  }
 }
 
 GlobalError::Severity ExtensionDisabledGlobalError::GetSeverity() {
@@ -237,8 +247,14 @@
 }
 
 base::string16 ExtensionDisabledGlobalError::MenuItemLabel() {
-  return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
-                                    base::UTF8ToUTF16(extension_->name()));
+  if (is_remote_install_) {
+    return l10n_util::GetStringFUTF16(
+        IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_TITLE,
+        base::UTF8ToUTF16(extension_->name()));
+  } else {
+    return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
+                                      base::UTF8ToUTF16(extension_->name()));
+  }
 }
 
 void ExtensionDisabledGlobalError::ExecuteMenuItem(Browser* browser) {
@@ -250,22 +266,39 @@
 }
 
 base::string16 ExtensionDisabledGlobalError::GetBubbleViewTitle() {
-  return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
-                                    base::UTF8ToUTF16(extension_->name()));
+  if (is_remote_install_) {
+    return l10n_util::GetStringFUTF16(
+        IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_TITLE,
+        base::UTF8ToUTF16(extension_->name()));
+  } else {
+    return l10n_util::GetStringFUTF16(IDS_EXTENSION_DISABLED_ERROR_TITLE,
+                                      base::UTF8ToUTF16(extension_->name()));
+  }
 }
 
 std::vector<base::string16>
 ExtensionDisabledGlobalError::GetBubbleViewMessages() {
   std::vector<base::string16> messages;
-  messages.push_back(l10n_util::GetStringFUTF16(
-      extension_->is_app() ?
-      IDS_APP_DISABLED_ERROR_LABEL : IDS_EXTENSION_DISABLED_ERROR_LABEL,
-      base::UTF8ToUTF16(extension_->name())));
-  messages.push_back(l10n_util::GetStringUTF16(
-      IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO));
   std::vector<base::string16> permission_warnings =
       extensions::PermissionMessageProvider::Get()->GetWarningMessages(
           extension_->GetActivePermissions(), extension_->GetType());
+  if (is_remote_install_) {
+    messages.push_back(l10n_util::GetStringFUTF16(
+        extension_->is_app()
+            ? IDS_APP_DISABLED_REMOTE_INSTALL_ERROR_LABEL
+            : IDS_EXTENSION_DISABLED_REMOTE_INSTALL_ERROR_LABEL,
+        base::UTF8ToUTF16(extension_->name())));
+    if (!permission_warnings.empty())
+      messages.push_back(
+          l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO));
+  } else {
+    messages.push_back(l10n_util::GetStringFUTF16(
+        extension_->is_app() ? IDS_APP_DISABLED_ERROR_LABEL
+                             : IDS_EXTENSION_DISABLED_ERROR_LABEL,
+        base::UTF8ToUTF16(extension_->name())));
+    messages.push_back(l10n_util::GetStringUTF16(
+        IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO));
+  }
   for (size_t i = 0; i < permission_warnings.size(); ++i) {
     messages.push_back(l10n_util::GetStringFUTF16(
         IDS_EXTENSION_PERMISSION_LINE, permission_warnings[i]));
@@ -274,7 +307,12 @@
 }
 
 base::string16 ExtensionDisabledGlobalError::GetBubbleViewAcceptButtonLabel() {
-  return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON);
+  if (is_remote_install_) {
+    return l10n_util::GetStringUTF16(
+        IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON);
+  } else {
+    return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON);
+  }
 }
 
 base::string16 ExtensionDisabledGlobalError::GetBubbleViewCancelButtonLabel() {
@@ -339,19 +377,21 @@
 
 void AddExtensionDisabledErrorWithIcon(base::WeakPtr<ExtensionService> service,
                                        const std::string& extension_id,
+                                       bool is_remote_install,
                                        const gfx::Image& icon) {
   if (!service.get())
     return;
   const Extension* extension = service->GetInstalledExtension(extension_id);
   if (extension) {
     GlobalErrorServiceFactory::GetForProfile(service->profile())
-        ->AddGlobalError(
-              new ExtensionDisabledGlobalError(service.get(), extension, icon));
+        ->AddGlobalError(new ExtensionDisabledGlobalError(
+            service.get(), extension, is_remote_install, icon));
   }
 }
 
 void AddExtensionDisabledError(ExtensionService* service,
-                               const Extension* extension) {
+                               const Extension* extension,
+                               bool is_remote_install) {
   // Do not display notifications for ephemeral apps that have been disabled.
   // Instead, a prompt will be shown the next time the app is launched.
   if (extension->is_ephemeral())
@@ -360,10 +400,14 @@
   extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
       extension, kIconSize, ExtensionIconSet::MATCH_BIGGER);
   gfx::Size size(kIconSize, kIconSize);
-  ImageLoader::Get(service->profile())->LoadImageAsync(
-      extension, image, size,
-      base::Bind(&AddExtensionDisabledErrorWithIcon,
-                 service->AsWeakPtr(), extension->id()));
+  ImageLoader::Get(service->profile())
+      ->LoadImageAsync(extension,
+                       image,
+                       size,
+                       base::Bind(&AddExtensionDisabledErrorWithIcon,
+                                  service->AsWeakPtr(),
+                                  extension->id(),
+                                  is_remote_install));
 }
 
 void ShowExtensionDisabledDialog(ExtensionService* service,
diff --git a/chrome/browser/extensions/extension_disabled_ui.h b/chrome/browser/extensions/extension_disabled_ui.h
index f71bcc9..08380585 100644
--- a/chrome/browser/extensions/extension_disabled_ui.h
+++ b/chrome/browser/extensions/extension_disabled_ui.h
@@ -17,8 +17,11 @@
 
 // Adds a global error to inform the user that an extension was
 // disabled after upgrading to higher permissions.
+// If |is_remote_install| is true, the extension was disabled because
+// it was installed remotely.
 void AddExtensionDisabledError(ExtensionService* service,
-                               const Extension* extension);
+                               const Extension* extension,
+                               bool is_remote_install);
 
 // Shows the extension install dialog.
 void ShowExtensionDisabledDialog(ExtensionService* service,
diff --git a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
index b6f58fd..5cd748a 100644
--- a/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
+++ b/chrome/browser/extensions/extension_disabled_ui_browsertest.cc
@@ -24,6 +24,8 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "net/url_request/url_fetcher.h"
+#include "sync/protocol/extension_specifics.pb.h"
+#include "sync/protocol/sync.pb.h"
 
 using extensions::Extension;
 using extensions::ExtensionRegistry;
@@ -234,3 +236,54 @@
                 ->GetDisableReasons(extension_id));
   EXPECT_TRUE(GetExtensionDisabledGlobalError());
 }
+
+// Test that an error appears if an extension gets installed server side.
+IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, RemoteInstall) {
+  static const char* extension_id = "pgdpcfcocojkjfbgpiianjngphoopgmo";
+  ExtensionSyncService* sync_service =
+      ExtensionSyncService::Get(browser()->profile());
+
+  // Note: This interceptor gets requests on the IO thread.
+  content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+  net::URLFetcher::SetEnableInterceptionForTests(true);
+  interceptor.SetResponseIgnoreQuery(
+      GURL("http://localhost/autoupdate/updates.xml"),
+      test_data_dir_.AppendASCII("permissions_increase")
+          .AppendASCII("updates.xml"));
+  interceptor.SetResponseIgnoreQuery(
+      GURL("http://localhost/autoupdate/v2.crx"),
+      scoped_temp_dir_.path().AppendASCII("permissions2.crx"));
+
+  extensions::ExtensionUpdater::CheckParams params;
+  service_->updater()->set_default_check_params(params);
+
+  sync_pb::EntitySpecifics specifics;
+  specifics.mutable_extension()->set_id(extension_id);
+  specifics.mutable_extension()->set_enabled(false);
+  specifics.mutable_extension()->set_remote_install(true);
+  specifics.mutable_extension()->set_update_url(
+      "http://localhost/autoupdate/updates.xml");
+  specifics.mutable_extension()->set_version("2");
+  syncer::SyncData sync_data =
+      syncer::SyncData::CreateRemoteData(1234567,
+                                         specifics,
+                                         base::Time::Now(),
+                                         syncer::AttachmentIdList(),
+                                         syncer::AttachmentServiceProxy());
+  // Sync is installing a new extension, so it pends.
+  EXPECT_FALSE(sync_service->ProcessExtensionSyncData(
+      extensions::ExtensionSyncData(sync_data)));
+
+  WaitForExtensionInstall();
+  content::BrowserThread::GetBlockingPool()->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+
+  const Extension* extension = service_->GetExtensionById(extension_id, true);
+  ASSERT_TRUE(extension);
+  EXPECT_EQ("2", extension->VersionString());
+  EXPECT_EQ(1u, registry_->disabled_extensions().size());
+  EXPECT_EQ(Extension::DISABLE_REMOTE_INSTALL,
+            ExtensionPrefs::Get(service_->profile())
+                ->GetDisableReasons(extension_id));
+  EXPECT_TRUE(GetExtensionDisabledGlobalError());
+}
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index 76f5a37..76cb03d 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -26,6 +26,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_prefs.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_icon_set.h"
@@ -53,75 +54,82 @@
 namespace {
 
 static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  0,  // The regular install prompt depends on what's being installed.
-  IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE,
-  IDS_EXTENSION_INSTALL_PROMPT_TITLE,
-  IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
-  IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE,
-  IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE,
-  IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE,
-  IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE,
+    0,  // The regular install prompt depends on what's being installed.
+    IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE,
+    IDS_EXTENSION_INSTALL_PROMPT_TITLE,
+    IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
+    IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE,
+    IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE,
+    IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE,
+    IDS_EXTENSION_LAUNCH_APP_PROMPT_TITLE,
+    0,  // The remote install prompt depends on what's being installed.
 };
 static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  IDS_EXTENSION_INSTALL_PROMPT_HEADING,
-  0,  // Inline installs use the extension name.
-  0,  // Heading for bundle installs depends on the bundle contents.
-  IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
-  IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING,
-  0,  // External installs use different strings for extensions/apps.
-  IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING,
-  IDS_EXTENSION_LAUNCH_APP_PROMPT_HEADING,
+    IDS_EXTENSION_INSTALL_PROMPT_HEADING,
+    0,  // Inline installs use the extension name.
+    0,  // Heading for bundle installs depends on the bundle contents.
+    IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
+    IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING,
+    0,  // External installs use different strings for extensions/apps.
+    IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING,
+    IDS_EXTENSION_LAUNCH_APP_PROMPT_HEADING,
+    IDS_EXTENSION_REMOTE_INSTALL_PROMPT_HEADING,
 };
 static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_CANCEL,
-  ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
+    ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
 };
 static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
-  IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
-  IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
-  IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
-  IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON,
-  0,  // External installs use different strings for extensions/apps.
-  IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON,
-  IDS_EXTENSION_PROMPT_LAUNCH_BUTTON,
+    IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
+    IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
+    IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
+    IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
+    IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON,
+    0,  // External installs use different strings for extensions/apps.
+    IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON,
+    IDS_EXTENSION_PROMPT_LAUNCH_BUTTON,
+    IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON,
 };
 static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  0,  // These all use the platform's default cancel label.
-  0,
-  0,
-  0,
-  IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON,
-  IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON,
-  IDS_CLOSE,
-  0,  // Platform dependent cancel button.
+    0,  // These all use the platform's default cancel label.
+    0,
+    0,
+    0,
+    IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON,
+    IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON,
+    IDS_CLOSE,
+    0,  // Platform dependent cancel button.
+    0,
 };
-static const int kPermissionsHeaderIds[
-    ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
-  IDS_EXTENSION_PROMPT_CAN_ACCESS,
-  IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
+static const int
+    kPermissionsHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
+        IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_CAN_ACCESS,
+        IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
+        IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
 };
 static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
-  IDS_EXTENSION_PROMPT_OAUTH_HEADER,
-  0,  // Inline installs don't show OAuth permissions.
-  0,  // Bundle installs don't show OAuth permissions.
-  IDS_EXTENSION_PROMPT_OAUTH_REENABLE_HEADER,
-  IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER,
-  0,
-  0,
-  IDS_EXTENSION_PROMPT_OAUTH_HEADER,
+    IDS_EXTENSION_PROMPT_OAUTH_HEADER,
+    0,  // Inline installs don't show OAuth permissions.
+    0,  // Bundle installs don't show OAuth permissions.
+    IDS_EXTENSION_PROMPT_OAUTH_REENABLE_HEADER,
+    IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER,
+    0,
+    0,
+    IDS_EXTENSION_PROMPT_OAUTH_HEADER,
+    IDS_EXTENSION_PROMPT_OAUTH_HEADER,
 };
 
 // Size of extension icon in top left of dialog.
@@ -280,6 +288,11 @@
   } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
     return l10n_util::GetStringFUTF16(
         resource_id, base::UTF8ToUTF16(extension_->name()));
+  } else if (type_ == REMOTE_INSTALL_PROMPT) {
+    if (extension_->is_app())
+      resource_id = IDS_EXTENSION_REMOTE_INSTALL_APP_PROMPT_TITLE;
+    else
+      resource_id = IDS_EXTENSION_REMOTE_INSTALL_EXTENSION_PROMPT_TITLE;
   }
 
   return l10n_util::GetStringUTF16(resource_id);
@@ -641,9 +654,16 @@
   extension_ = extension;
   permissions_ = extension->GetActivePermissions();
   delegate_ = delegate;
-  prompt_.set_type(extension->is_ephemeral() ? LAUNCH_PROMPT :
-                                               RE_ENABLE_PROMPT);
-
+  bool is_remote_install =
+      install_ui_->profile() &&
+      extensions::ExtensionPrefs::Get(install_ui_->profile())->HasDisableReason(
+          extension->id(), extensions::Extension::DISABLE_REMOTE_INSTALL);
+  if (extension->is_ephemeral())
+    prompt_.set_type(LAUNCH_PROMPT);
+  else if (is_remote_install)
+    prompt_.set_type(REMOTE_INSTALL_PROMPT);
+  else
+    prompt_.set_type(RE_ENABLE_PROMPT);
   LoadImageIfNeeded();
 }
 
@@ -827,7 +847,8 @@
     case EXTERNAL_INSTALL_PROMPT:
     case INSTALL_PROMPT:
     case LAUNCH_PROMPT:
-    case POST_INSTALL_PERMISSIONS_PROMPT: {
+    case POST_INSTALL_PERMISSIONS_PROMPT:
+    case REMOTE_INSTALL_PROMPT: {
       prompt_.set_extension(extension_);
       prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
       break;
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h
index 4de81862..c8d91e35 100644
--- a/chrome/browser/extensions/extension_install_prompt.h
+++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -67,6 +67,7 @@
     EXTERNAL_INSTALL_PROMPT,
     POST_INSTALL_PERMISSIONS_PROMPT,
     LAUNCH_PROMPT,
+    REMOTE_INSTALL_PROMPT,
     NUM_PROMPT_TYPES
   };
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 4cb01bf..5463968 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -547,6 +547,8 @@
     installer->set_install_source(pending_extension_info->install_source());
     if (pending_extension_info->install_silently())
       installer->set_allow_silent_install(true);
+    if (pending_extension_info->remote_install())
+      installer->set_grant_permissions(false);
     creation_flags = pending_extension_info->creation_flags();
     if (pending_extension_info->mark_acknowledged())
       AcknowledgeExternalExtension(id);
@@ -1495,11 +1497,17 @@
         content::Source<Profile>(profile_),
         content::Details<const Extension>(extension));
 
-    // Show the extension disabled error if a permissions increase was the
-    // only reason it was disabled.
-    if (extension_prefs_->GetDisableReasons(extension->id()) ==
-        Extension::DISABLE_PERMISSIONS_INCREASE) {
-      extensions::AddExtensionDisabledError(this, extension);
+    // Show the extension disabled error if a permissions increase or a remote
+    // installation is the reason it was disabled, and no other reasons exist.
+    int reasons = extension_prefs_->GetDisableReasons(extension->id());
+    const int kReasonMask = Extension::DISABLE_PERMISSIONS_INCREASE |
+                            Extension::DISABLE_REMOTE_INSTALL;
+    if (reasons & kReasonMask && !(reasons & ~kReasonMask)) {
+      extensions::AddExtensionDisabledError(
+          this,
+          extension,
+          extension_prefs_->HasDisableReason(
+              extension->id(), Extension::DISABLE_REMOTE_INSTALL));
     }
   } else if (reloading) {
     // Replace the old extension with the new version.
@@ -1661,8 +1669,13 @@
   }
 
   // Extension has changed permissions significantly. Disable it. A
-  // notification should be sent by the caller.
-  if (is_privilege_increase) {
+  // notification should be sent by the caller. If the extension is already
+  // disabled because it was installed remotely, don't add another disable
+  // reason, but instead always set the "did escalate permissions" flag, to
+  // ensure enabling it will always show a warning.
+  if (disable_reasons == Extension::DISABLE_REMOTE_INSTALL) {
+    extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
+  } else if (is_privilege_increase) {
     disable_reasons |= Extension::DISABLE_PERMISSIONS_INCREASE;
     if (!extension_prefs_->DidExtensionEscalatePermissions(extension->id())) {
       RecordPermissionMessagesHistogram(
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 41fea0f..ce441ed 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -3027,10 +3027,14 @@
   const std::string kFakeId(all_zero);
   const GURL kFakeUpdateURL("http:://fake.update/url");
   const bool kFakeInstallSilently(true);
+  const bool kFakeRemoteInstall(false);
 
-  EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      kFakeId, kFakeUpdateURL, &IsExtension,
-      kFakeInstallSilently));
+  EXPECT_TRUE(
+      service_->pending_extension_manager()->AddFromSync(kFakeId,
+                                                         kFakeUpdateURL,
+                                                         &IsExtension,
+                                                         kFakeInstallSilently,
+                                                         kFakeRemoteInstall));
 
   const extensions::PendingExtensionInfo* pending_extension_info;
   ASSERT_TRUE((pending_extension_info = service_->pending_extension_manager()->
@@ -3038,6 +3042,7 @@
   EXPECT_EQ(kFakeUpdateURL, pending_extension_info->update_url());
   EXPECT_EQ(&IsExtension, pending_extension_info->should_allow_install_);
   EXPECT_EQ(kFakeInstallSilently, pending_extension_info->install_silently());
+  EXPECT_EQ(kFakeRemoteInstall, pending_extension_info->remote_install());
 }
 
 namespace {
@@ -3045,14 +3050,18 @@
 const char kGoodUpdateURL[] = "http://good.update/url";
 const bool kGoodIsFromSync = true;
 const bool kGoodInstallSilently = true;
+const bool kGoodRemoteInstall = false;
 }  // namespace
 
 // Test updating a pending extension.
 TEST_F(ExtensionServiceTest, UpdatePendingExtension) {
   InitializeEmptyExtensionService();
-  EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      kGoodId, GURL(kGoodUpdateURL), &IsExtension,
-      kGoodInstallSilently));
+  EXPECT_TRUE(
+      service_->pending_extension_manager()->AddFromSync(kGoodId,
+                                                         GURL(kGoodUpdateURL),
+                                                         &IsExtension,
+                                                         kGoodInstallSilently,
+                                                         kGoodRemoteInstall));
   EXPECT_TRUE(service_->pending_extension_manager()->IsIdPending(kGoodId));
 
   base::FilePath path = data_dir_.AppendASCII("good.crx");
@@ -3077,7 +3086,7 @@
 TEST_F(ExtensionServiceTest, DISABLED_UpdatePendingTheme) {
   InitializeEmptyExtensionService();
   EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      theme_crx, GURL(), &IsTheme, false));
+      theme_crx, GURL(), &IsTheme, false, false));
   EXPECT_TRUE(service_->pending_extension_manager()->IsIdPending(theme_crx));
 
   base::FilePath path = data_dir_.AppendASCII("theme.crx");
@@ -3136,9 +3145,12 @@
   InitializeEmptyExtensionService();
 
   // Add a crx to be installed from the update mechanism.
-  EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      kGoodId, GURL(kGoodUpdateURL), &IsExtension,
-      kGoodInstallSilently));
+  EXPECT_TRUE(
+      service_->pending_extension_manager()->AddFromSync(kGoodId,
+                                                         GURL(kGoodUpdateURL),
+                                                         &IsExtension,
+                                                         kGoodInstallSilently,
+                                                         kGoodRemoteInstall));
 
   // Check that there is a pending crx, with is_from_sync set to true.
   const extensions::PendingExtensionInfo* pending_extension_info;
@@ -3163,9 +3175,12 @@
             pending_extension_info->install_source());
 
   // Add a crx to be installed from the update mechanism.
-  EXPECT_FALSE(service_->pending_extension_manager()->AddFromSync(
-      kGoodId, GURL(kGoodUpdateURL), &IsExtension,
-      kGoodInstallSilently));
+  EXPECT_FALSE(
+      service_->pending_extension_manager()->AddFromSync(kGoodId,
+                                                         GURL(kGoodUpdateURL),
+                                                         &IsExtension,
+                                                         kGoodInstallSilently,
+                                                         kGoodRemoteInstall));
 
   // Check that the external, non-sync update was not overridden.
   ASSERT_TRUE((pending_extension_info = service_->pending_extension_manager()->
@@ -3180,7 +3195,7 @@
 TEST_F(ExtensionServiceTest, UpdatePendingCrxThemeMismatch) {
   InitializeEmptyExtensionService();
   EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      theme_crx, GURL(), &IsExtension, true));
+      theme_crx, GURL(), &IsExtension, true, false));
 
   EXPECT_TRUE(service_->pending_extension_manager()->IsIdPending(theme_crx));
 
@@ -3201,8 +3216,12 @@
 TEST_F(ExtensionServiceTest, UpdatePendingExtensionFailedShouldInstallTest) {
   InitializeEmptyExtensionService();
   // Add pending extension with a flipped is_theme.
-  EXPECT_TRUE(service_->pending_extension_manager()->AddFromSync(
-      kGoodId, GURL(kGoodUpdateURL), &IsTheme, kGoodInstallSilently));
+  EXPECT_TRUE(
+      service_->pending_extension_manager()->AddFromSync(kGoodId,
+                                                         GURL(kGoodUpdateURL),
+                                                         &IsTheme,
+                                                         kGoodInstallSilently,
+                                                         kGoodRemoteInstall));
   EXPECT_TRUE(service_->pending_extension_manager()->IsIdPending(kGoodId));
 
   base::FilePath path = data_dir_.AppendASCII("good.crx");
@@ -3249,7 +3268,8 @@
       kGoodInstallSilently,
       Manifest::INTERNAL,
       Extension::NO_FLAGS,
-      false);
+      false,
+      kGoodRemoteInstall);
   UpdateExtension(good->id(), path, ENABLED);
 
   EXPECT_FALSE(service_->pending_extension_manager()->IsIdPending(kGoodId));
@@ -5461,7 +5481,8 @@
   const Extension* extension = service_->GetExtensionById(good0, true);
   ASSERT_TRUE(extension);
   ASSERT_TRUE(service_->IsExtensionEnabled(good0));
-  extensions::ExtensionSyncData disable_good_crx(*extension, false, false);
+  extensions::ExtensionSyncData disable_good_crx(
+      *extension, false, false, false);
 
   // Then sync data arrives telling us to disable |good0|.
   syncer::SyncDataList sync_data;
@@ -5508,7 +5529,8 @@
 
   // Now sync data comes in that says to disable good0. This should be
   // ignored.
-  extensions::ExtensionSyncData disable_good_crx(*extension, false, false);
+  extensions::ExtensionSyncData disable_good_crx(
+      *extension, false, false, false);
   syncer::SyncDataList sync_data;
   sync_data.push_back(disable_good_crx.GetSyncData());
   extension_sync_service_->MergeDataAndStartSyncing(
@@ -6508,7 +6530,11 @@
   // Fake a request from sync to install an extension.
   bool AddPendingSyncInstall() {
     return service_->pending_extension_manager()->AddFromSync(
-        crx_id_, GURL(kGoodUpdateURL), &IsExtension, kGoodInstallSilently);
+        crx_id_,
+        GURL(kGoodUpdateURL),
+        &IsExtension,
+        kGoodInstallSilently,
+        kGoodRemoteInstall);
   }
 
   // Fake a policy install.
diff --git a/chrome/browser/extensions/extension_sync_data.cc b/chrome/browser/extensions/extension_sync_data.cc
index 0ebe72e..aaf1244 100644
--- a/chrome/browser/extensions/extension_sync_data.cc
+++ b/chrome/browser/extensions/extension_sync_data.cc
@@ -31,8 +31,8 @@
 }
 
 ExtensionSyncData::ExtensionSyncData(const syncer::SyncChange& sync_change)
-    : uninstalled_(
-        sync_change.change_type() == syncer::SyncChange::ACTION_DELETE),
+    : uninstalled_(sync_change.change_type() ==
+                   syncer::SyncChange::ACTION_DELETE),
       enabled_(false),
       incognito_enabled_(false),
       remote_install_(false) {
@@ -41,12 +41,13 @@
 
 ExtensionSyncData::ExtensionSyncData(const Extension& extension,
                                      bool enabled,
-                                     bool incognito_enabled)
-   :  id_(extension.id()),
+                                     bool incognito_enabled,
+                                     bool remote_install)
+    : id_(extension.id()),
       uninstalled_(false),
       enabled_(enabled),
       incognito_enabled_(incognito_enabled),
-      remote_install_(false),
+      remote_install_(remote_install),
       version_(extension.from_bookmark() ? base::Version("0")
                                          : *extension.version()),
       update_url_(ManifestURL::GetUpdateURL(&extension)),
diff --git a/chrome/browser/extensions/extension_sync_data.h b/chrome/browser/extensions/extension_sync_data.h
index 04c4b18d..9165e75 100644
--- a/chrome/browser/extensions/extension_sync_data.h
+++ b/chrome/browser/extensions/extension_sync_data.h
@@ -31,7 +31,8 @@
   explicit ExtensionSyncData(const syncer::SyncChange& sync_change);
   ExtensionSyncData(const Extension& extension,
                     bool enabled,
-                    bool incognito_enabled);
+                    bool incognito_enabled,
+                    bool remote_install);
   ~ExtensionSyncData();
 
   // Retrieve sync data from this class.
@@ -56,6 +57,7 @@
   bool uninstalled() const { return uninstalled_; }
   bool enabled() const { return enabled_; }
   bool incognito_enabled() const { return incognito_enabled_; }
+  bool remote_install() const { return remote_install_; }
 
   // Version-dependent properties (i.e., should be used only when the
   // version of the currenty-installed extension matches |version|).
diff --git a/chrome/browser/extensions/extension_sync_service.cc b/chrome/browser/extensions/extension_sync_service.cc
index 7dbe613..8ac5c07 100644
--- a/chrome/browser/extensions/extension_sync_service.cc
+++ b/chrome/browser/extensions/extension_sync_service.cc
@@ -254,7 +254,9 @@
   return extensions::ExtensionSyncData(
       extension,
       extension_service_->IsExtensionEnabled(extension.id()),
-      extensions::util::IsIncognitoEnabled(extension.id(), profile_));
+      extensions::util::IsIncognitoEnabled(extension.id(), profile_),
+      extension_prefs_->HasDisableReason(extension.id(),
+                                         Extension::DISABLE_REMOTE_INSTALL));
 }
 
 extensions::AppSyncData ExtensionSyncService::GetAppSyncData(
@@ -263,6 +265,8 @@
       extension,
       extension_service_->IsExtensionEnabled(extension.id()),
       extensions::util::IsIncognitoEnabled(extension.id(), profile_),
+      extension_prefs_->HasDisableReason(extension.id(),
+                                         Extension::DISABLE_REMOTE_INSTALL),
       extension_prefs_->app_sorting()->GetAppLaunchOrdinal(extension.id()),
       extension_prefs_->app_sorting()->GetPageOrdinal(extension.id()),
       extensions::GetLaunchTypePrefValue(extension_prefs_, extension.id()));
@@ -465,11 +469,21 @@
   // been installed yet, so we don't know if the disable reason was a
   // permissions increase.  That will be updated once CheckPermissionsIncrease
   // is called for it.
+  // However if the extension is marked as a remote install in sync, we know
+  // what the disable reason is, so set it to that directly. Note that when
+  // CheckPermissionsIncrease runs, it might still add permissions increase
+  // as a disable reason for the extension.
   if (extension_sync_data.enabled())
     extension_service_->EnableExtension(id);
-  else if (!IsPendingEnable(id))
-    extension_service_->DisableExtension(
-        id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
+  else if (!IsPendingEnable(id)) {
+    if (extension_sync_data.remote_install()) {
+      extension_service_->DisableExtension(id,
+                                           Extension::DISABLE_REMOTE_INSTALL);
+    } else {
+      extension_service_->DisableExtension(
+          id, Extension::DISABLE_UNKNOWN_FROM_SYNC);
+    }
+  }
 
   // We need to cache some version information here because setting the
   // incognito flag invalidates the |extension| pointer (it reloads the
@@ -501,7 +515,8 @@
             id,
             extension_sync_data.update_url(),
             filter,
-            kInstallSilently)) {
+            kInstallSilently,
+            extension_sync_data.remote_install())) {
       LOG(WARNING) << "Could not add pending extension for " << id;
       // This means that the extension is already pending installation, with a
       // non-INTERNAL location.  Add to pending_sync_data, even though it will
diff --git a/chrome/browser/extensions/pending_extension_info.cc b/chrome/browser/extensions/pending_extension_info.cc
index 0fa105b..b0c052f0 100644
--- a/chrome/browser/extensions/pending_extension_info.cc
+++ b/chrome/browser/extensions/pending_extension_info.cc
@@ -18,7 +18,8 @@
     bool install_silently,
     Manifest::Location install_source,
     int creation_flags,
-    bool mark_acknowledged)
+    bool mark_acknowledged,
+    bool remote_install)
     : id_(id),
       update_url_(update_url),
       version_(version),
@@ -28,14 +29,20 @@
       install_silently_(install_silently),
       install_source_(install_source),
       creation_flags_(creation_flags),
-      mark_acknowledged_(mark_acknowledged) {}
+      mark_acknowledged_(mark_acknowledged),
+      remote_install_(remote_install) {
+}
 
 PendingExtensionInfo::PendingExtensionInfo()
     : update_url_(),
       should_allow_install_(NULL),
       is_from_sync_(true),
       install_silently_(false),
-      install_source_(Manifest::INVALID_LOCATION) {}
+      install_source_(Manifest::INVALID_LOCATION),
+      creation_flags_(0),
+      mark_acknowledged_(false),
+      remote_install_(false) {
+}
 
 PendingExtensionInfo::~PendingExtensionInfo() {}
 
diff --git a/chrome/browser/extensions/pending_extension_info.h b/chrome/browser/extensions/pending_extension_info.h
index a3ea47b1..11c159a 100644
--- a/chrome/browser/extensions/pending_extension_info.h
+++ b/chrome/browser/extensions/pending_extension_info.h
@@ -36,7 +36,8 @@
                        bool install_silently,
                        Manifest::Location install_source,
                        int creation_flags,
-                       bool mark_acknowledged);
+                       bool mark_acknowledged,
+                       bool remote_install);
 
   // Required for STL container membership.  Should not be used directly.
   PendingExtensionInfo();
@@ -65,6 +66,7 @@
   Manifest::Location install_source() const { return install_source_; }
   int creation_flags() const { return creation_flags_; }
   bool mark_acknowledged() const { return mark_acknowledged_; }
+  bool remote_install() const { return remote_install_; }
 
   // Returns -1, 0 or 1 if |this| has lower, equal or higher precedence than
   // |other|, respectively. "Equal" precedence means that the version and the
@@ -90,6 +92,7 @@
   Manifest::Location install_source_;
   int creation_flags_;
   bool mark_acknowledged_;
+  bool remote_install_;
 
   FRIEND_TEST_ALL_PREFIXES(::ExtensionServiceTest, AddPendingExtensionFromSync);
 };
diff --git a/chrome/browser/extensions/pending_extension_manager.cc b/chrome/browser/extensions/pending_extension_manager.cc
index 1a4ed42..c91c1fea 100644
--- a/chrome/browser/extensions/pending_extension_manager.cc
+++ b/chrome/browser/extensions/pending_extension_manager.cc
@@ -89,7 +89,8 @@
     const std::string& id,
     const GURL& update_url,
     PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
-    bool install_silently) {
+    bool install_silently,
+    bool remote_install) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (service_.GetInstalledExtension(id)) {
@@ -119,7 +120,8 @@
                           install_silently,
                           kSyncLocation,
                           Extension::NO_FLAGS,
-                          kMarkAcknowledged);
+                          kMarkAcknowledged,
+                          remote_install);
 }
 
 bool PendingExtensionManager::AddFromExtensionImport(
@@ -138,6 +140,7 @@
   const bool kInstallSilently = true;
   const Manifest::Location kManifestLocation = Manifest::INTERNAL;
   const bool kMarkAcknowledged = false;
+  const bool kRemoteInstall = false;
 
   return AddExtensionImpl(id,
                           std::string(),
@@ -148,7 +151,8 @@
                           kInstallSilently,
                           kManifestLocation,
                           Extension::NO_FLAGS,
-                          kMarkAcknowledged);
+                          kMarkAcknowledged,
+                          kRemoteInstall);
 }
 
 bool PendingExtensionManager::AddFromExternalUpdateUrl(
@@ -162,6 +166,7 @@
 
   const bool kIsFromSync = false;
   const bool kInstallSilently = true;
+  const bool kRemoteInstall = false;
 
   const Extension* extension = service_.GetInstalledExtension(id);
   if (extension && location == Manifest::GetHigherPriorityLocation(
@@ -188,7 +193,8 @@
                           kInstallSilently,
                           location,
                           creation_flags,
-                          mark_acknowledged);
+                          mark_acknowledged,
+                          kRemoteInstall);
 }
 
 
@@ -203,8 +209,9 @@
   // made sure it is not installed.  Make all AddFrom*() methods
   // consistent.
   GURL kUpdateUrl = GURL();
-  bool kIsFromSync = false;
-  bool kInstallSilently = true;
+  const bool kIsFromSync = false;
+  const bool kInstallSilently = true;
+  const bool kRemoteInstall = false;
 
   return AddExtensionImpl(id,
                           std::string(),
@@ -215,7 +222,8 @@
                           kInstallSilently,
                           install_source,
                           creation_flags,
-                          mark_acknowledged);
+                          mark_acknowledged,
+                          kRemoteInstall);
 }
 
 void PendingExtensionManager::GetPendingIdsForUpdateCheck(
@@ -247,7 +255,8 @@
     bool install_silently,
     Manifest::Location install_source,
     int creation_flags,
-    bool mark_acknowledged) {
+    bool mark_acknowledged,
+    bool remote_install) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   PendingExtensionInfo info(id,
@@ -259,7 +268,8 @@
                             install_silently,
                             install_source,
                             creation_flags,
-                            mark_acknowledged);
+                            mark_acknowledged,
+                            remote_install);
 
   if (const PendingExtensionInfo* pending = GetById(id)) {
     // Bugs in this code will manifest as sporadic incorrect extension
diff --git a/chrome/browser/extensions/pending_extension_manager.h b/chrome/browser/extensions/pending_extension_manager.h
index 96dfa6e..bf2e1fc7 100644
--- a/chrome/browser/extensions/pending_extension_manager.h
+++ b/chrome/browser/extensions/pending_extension_manager.h
@@ -84,7 +84,8 @@
       const std::string& id,
       const GURL& update_url,
       PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
-      bool install_silently);
+      bool install_silently,
+      bool remote_install);
 
   // Adds an extension that was depended on by another extension.
   bool AddFromExtensionImport(
@@ -132,7 +133,8 @@
       bool install_silently,
       Manifest::Location install_source,
       int creation_flags,
-      bool mark_acknowledged);
+      bool mark_acknowledged,
+      bool remote_install);
 
   // Add a pending extension record directly.  Used for unit tests that need
   // to set an inital state. Use friendship to allow the tests to call this
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index 6bdf5e4..d1d42871 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -352,6 +352,7 @@
     const bool kIsFromSync = true;
     const bool kInstallSilently = true;
     const bool kMarkAcknowledged = false;
+    const bool kRemoteInstall = false;
     std::string id = id_util::GenerateId(base::StringPrintf("extension%i", i));
 
     pending_extension_manager->AddForTesting(
@@ -364,7 +365,8 @@
                              kInstallSilently,
                              Manifest::INTERNAL,
                              Extension::NO_FLAGS,
-                             kMarkAcknowledged));
+                             kMarkAcknowledged,
+                             kRemoteInstall));
   }
 }
 
@@ -1056,6 +1058,7 @@
       const bool kIsFromSync = true;
       const bool kInstallSilently = true;
       const bool kMarkAcknowledged = false;
+      const bool kRemoteInstall = false;
       PendingExtensionManager* pending_extension_manager =
           service->pending_extension_manager();
       pending_extension_manager->AddForTesting(
@@ -1068,7 +1071,8 @@
                                kInstallSilently,
                                Manifest::INTERNAL,
                                Extension::NO_FLAGS,
-                               kMarkAcknowledged));
+                               kMarkAcknowledged,
+                               kRemoteInstall));
     }
 
     // Call back the ExtensionUpdater with a 200 response and some test data
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 4e1464a..74ddb900 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
@@ -501,6 +501,7 @@
       *extension,
       original_data.extension_sync_data().enabled(),
       original_data.extension_sync_data().incognito_enabled(),
+      original_data.extension_sync_data().remote_install(),
       original_data.app_launch_ordinal(),
       original_data.page_ordinal(),
       extensions::NUM_LAUNCH_TYPES);
diff --git a/chrome/browser/themes/theme_syncable_service.cc b/chrome/browser/themes/theme_syncable_service.cc
index fe14051..82550f9a 100644
--- a/chrome/browser/themes/theme_syncable_service.cc
+++ b/chrome/browser/themes/theme_syncable_service.cc
@@ -235,8 +235,9 @@
       // so by adding it as a pending extension and then triggering an
       // auto-update cycle.
       const bool kInstallSilently = true;
+      const bool kRemoteInstall = false;
       if (!extensions_service->pending_extension_manager()->AddFromSync(
-              id, update_url, &IsTheme, kInstallSilently)) {
+              id, update_url, &IsTheme, kInstallSilently, kRemoteInstall)) {
         LOG(WARNING) << "Could not add pending extension for " << id;
         return;
       }