Temporarily disable extensions and sync while a profile is locked.

Place all the heavy lifting and logic in profiles code, with extensions providing a new collection for holding "locked" extensions.

This replaces CL 697143003.

BUG=354124

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

Cr-Commit-Position: refs/heads/master@{#305897}
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 5f8a3e8..338a076 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -458,10 +458,11 @@
     case UnloadedExtensionInfo::REASON_TERMINATE:  // Fall through.
     case UnloadedExtensionInfo::REASON_UNINSTALL:  // Fall through.
     case UnloadedExtensionInfo::REASON_BLACKLIST:  // Fall through.
+    case UnloadedExtensionInfo::REASON_LOCK_ALL:   // Fall through.
     case UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN:
       ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16(extension->id()));
       SendChangeNotification(Profile::FromBrowserContext(browser_context));
-      break;
+      return;
     case UnloadedExtensionInfo::REASON_UPDATE: {
       // If there is a manifest specified background page, then shut it down
       // here, since if the updated extension still has the background page,
@@ -473,13 +474,15 @@
         ShutdownAssociatedBackgroundContents(
             base::ASCIIToUTF16(extension->id()));
       }
+      return;
+    case UnloadedExtensionInfo::REASON_UNDEFINED:
+      // Fall through to undefined case.
       break;
     }
-    default:
-      NOTREACHED();
-      ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16(extension->id()));
-      break;
   }
+  NOTREACHED() << "Undefined case " << reason;
+  return ShutdownAssociatedBackgroundContents(
+      base::ASCIIToUTF16(extension->id()));
 }
 
 void BackgroundContentsService::OnExtensionUninstalled(
diff --git a/chrome/browser/extensions/api/screenlock_private/screenlock_private_apitest.cc b/chrome/browser/extensions/api/screenlock_private/screenlock_private_apitest.cc
index 0c22acd..66256377 100644
--- a/chrome/browser/extensions/api/screenlock_private/screenlock_private_apitest.cc
+++ b/chrome/browser/extensions/api/screenlock_private/screenlock_private_apitest.cc
@@ -73,14 +73,9 @@
     }
   }
 
-  // Loads |extension_name| as appropriate for the platform and waits for a
-  // pass / fail notification.
+  // Loads |extension_name| and waits for a pass / fail notification.
   void RunTest(const std::string& extension_name) {
-#if defined(OS_CHROMEOS)
     ASSERT_TRUE(RunComponentExtensionTest(extension_name)) << message_;
-#else
-    ASSERT_TRUE(RunExtensionTest(extension_name)) << message_;
-#endif
   }
 
  private:
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 390092d..b4e4820 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -167,10 +167,10 @@
 
 void ExtensionService::BlacklistExtensionForTest(
     const std::string& extension_id) {
-  ExtensionIdSet blocked;
+  ExtensionIdSet blacklisted;
   ExtensionIdSet unchanged;
-  blocked.insert(extension_id);
-  UpdateBlockedExtensions(blocked, unchanged);
+  blacklisted.insert(extension_id);
+  UpdateBlacklistedExtensions(blacklisted, unchanged);
 }
 
 bool ExtensionService::OnExternalExtensionUpdateUrlFound(
@@ -270,6 +270,7 @@
       browser_terminating_(false),
       installs_delayed_for_gc_(false),
       is_first_run_(false),
+      block_extensions_(false),
       shared_module_service_(new extensions::SharedModuleService(profile_)) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
@@ -370,11 +371,11 @@
     const std::string& id, bool include_disabled) const {
   int include_mask = ExtensionRegistry::ENABLED;
   if (include_disabled) {
-    // Include blacklisted extensions here because there are hundreds of
-    // callers of this function, and many might assume that this includes those
-    // that have been disabled due to blacklisting.
+    // Include blacklisted and blocked extensions here because there are
+    // hundreds of callers of this function, and many might assume that this
+    // includes those that have been disabled due to blacklisting or blocking.
     include_mask |= ExtensionRegistry::DISABLED |
-                    ExtensionRegistry::BLACKLISTED;
+                    ExtensionRegistry::BLACKLISTED | ExtensionRegistry::BLOCKED;
   }
   return registry_->GetExtensionById(id, include_mask);
 }
@@ -575,12 +576,14 @@
     return;
   }
 
-  // Ignore attempts to reload a blacklisted extension. Sometimes this can
-  // happen in a convoluted reload sequence triggered by the termination of a
-  // blacklisted extension and a naive attempt to reload it. For an example see
-  // http://crbug.com/373842.
-  if (registry_->blacklisted_extensions().Contains(transient_extension_id))
+  // Ignore attempts to reload a blacklisted or blocked extension. Sometimes
+  // this can happen in a convoluted reload sequence triggered by the
+  // termination of a blacklisted or blocked extension and a naive attempt to
+  // reload it. For an example see http://crbug.com/373842.
+  if (registry_->blacklisted_extensions().Contains(transient_extension_id) ||
+      registry_->blocked_extensions().Contains(transient_extension_id)) {
     return;
+  }
 
   base::FilePath path;
 
@@ -785,10 +788,15 @@
   }
 
   if (registry_->disabled_extensions().Contains(extension_id) ||
-      registry_->blacklisted_extensions().Contains(extension_id)) {
+      registry_->blacklisted_extensions().Contains(extension_id) ||
+      registry_->blocked_extensions().Contains(extension_id)) {
     return false;
   }
 
+  if (block_extensions_ &&
+      CanBlockExtension(GetInstalledExtension(extension_id)))
+    return false;
+
   // If the extension hasn't been loaded yet, check the prefs for it. Assume
   // enabled unless otherwise noted.
   return !extension_prefs_->IsExtensionDisabled(extension_id) &&
@@ -916,6 +924,48 @@
   }
 }
 
+// Extensions that are not locked, components or forced by policy should be
+// locked. Extensions are no longer considered enabled or disabled. Blacklisted
+// extensions are now considered both blacklisted and locked.
+void ExtensionService::BlockAllExtensions() {
+  if (block_extensions_)
+    return;
+  block_extensions_ = true;
+
+  // Blacklisted extensions are already unloaded, need not be blocked.
+  scoped_ptr<ExtensionSet> extensions =
+      registry_->GenerateInstalledExtensionsSet(ExtensionRegistry::ENABLED |
+                                                ExtensionRegistry::DISABLED |
+                                                ExtensionRegistry::TERMINATED);
+
+  for (const scoped_refptr<const Extension>& extension : *extensions) {
+    const std::string& id = extension->id();
+
+    if (!CanBlockExtension(extension.get()))
+      continue;
+
+    registry_->RemoveEnabled(id);
+    registry_->RemoveDisabled(id);
+    registry_->RemoveTerminated(id);
+
+    registry_->AddBlocked(extension.get());
+    UnloadExtension(id, extensions::UnloadedExtensionInfo::REASON_LOCK_ALL);
+  }
+}
+
+// All locked extensions should revert to being either enabled or disabled
+// as appropriate.
+void ExtensionService::UnblockAllExtensions() {
+  block_extensions_ = false;
+  scoped_ptr<ExtensionSet> to_unblock =
+      registry_->GenerateInstalledExtensionsSet(ExtensionRegistry::BLOCKED);
+
+  for (const scoped_refptr<const Extension>& extension : *to_unblock) {
+    registry_->RemoveBlocked(extension->id());
+    AddExtension(extension.get());
+  }
+}
+
 void ExtensionService::GrantPermissionsAndEnableExtension(
     const Extension* extension) {
   GrantPermissions(extension);
@@ -1371,6 +1421,8 @@
     // installation then threads through the install and pending install flow
     // of this class, and we check when loading installed extensions.
     registry_->AddBlacklisted(extension);
+  } else if (block_extensions_ && CanBlockExtension(extension)) {
+    registry_->AddBlocked(extension);
   } else if (!reloading &&
              extension_prefs_->IsExtensionDisabled(extension->id())) {
     registry_->AddDisabled(extension);
@@ -2194,6 +2246,13 @@
   return Extension::DISABLE_NONE;
 }
 
+// Helper method to determine if an extension can be blocked.
+bool ExtensionService::CanBlockExtension(const Extension* extension) const {
+  return extension->location() != Manifest::COMPONENT &&
+         extension->location() != Manifest::EXTERNAL_COMPONENT &&
+         !system_->management_policy()->MustRemainEnabled(extension, NULL);
+}
+
 bool ExtensionService::ShouldDelayExtensionUpdate(
     const std::string& extension_id,
     bool install_immediately) const {
@@ -2255,7 +2314,7 @@
     const extensions::Blacklist::BlacklistStateMap& state_map) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  std::set<std::string> blocked;
+  std::set<std::string> blacklisted;
   ExtensionIdSet greylist;
   ExtensionIdSet unchanged;
   for (extensions::Blacklist::BlacklistStateMap::const_iterator it =
@@ -2267,7 +2326,7 @@
         break;
 
       case extensions::BLACKLISTED_MALWARE:
-        blocked.insert(it->first);
+        blacklisted.insert(it->first);
         break;
 
       case extensions::BLACKLISTED_SECURITY_VULNERABILITY:
@@ -2282,7 +2341,7 @@
     }
   }
 
-  UpdateBlockedExtensions(blocked, unchanged);
+  UpdateBlacklistedExtensions(blacklisted, unchanged);
   UpdateGreylistedExtensions(greylist, unchanged, state_map);
 
   error_controller_->ShowErrorIfNeeded();
@@ -2300,21 +2359,20 @@
 }
 }  // namespace
 
-void ExtensionService::UpdateBlockedExtensions(
-    const ExtensionIdSet& blocked,
+void ExtensionService::UpdateBlacklistedExtensions(
+    const ExtensionIdSet& blacklisted,
     const ExtensionIdSet& unchanged) {
   ExtensionIdSet not_yet_blocked, no_longer_blocked;
-  Partition(registry_->blacklisted_extensions().GetIDs(),
-            blocked, unchanged,
-            &no_longer_blocked, &not_yet_blocked);
+  Partition(registry_->blacklisted_extensions().GetIDs(), blacklisted,
+            unchanged, &no_longer_blocked, &not_yet_blocked);
 
   for (ExtensionIdSet::iterator it = no_longer_blocked.begin();
        it != no_longer_blocked.end(); ++it) {
     scoped_refptr<const Extension> extension =
         registry_->blacklisted_extensions().GetByID(*it);
     if (!extension.get()) {
-      NOTREACHED() << "Extension " << *it << " no longer blocked, "
-                   << "but it was never blocked.";
+      NOTREACHED() << "Extension " << *it << " no longer blacklisted, "
+                   << "but it was never blacklisted.";
       continue;
     }
     registry_->RemoveBlacklisted(*it);
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 5d9ecc9..8f760f95 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -289,6 +289,18 @@
   // |was_installed_by_default| flag.
   void DisableUserExtensions(const std::vector<std::string>& except_ids);
 
+  // Puts all extensions in a blocked state: Unloading every extension, and
+  // preventing them from ever loading until UnblockAllExtensions is called.
+  // This state is stored in preferences, so persists until Chrome restarts.
+  //
+  // Component, external component and whitelisted policy installed extensions
+  // are exempt from being Blocked (see CanBlockExtension).
+  void BlockAllExtensions();
+
+  // All blocked extensions are reverted to their previous state, and are
+  // reloaded. Newly added extensions are no longer automatically blocked.
+  void UnblockAllExtensions();
+
   // Updates the |extension|'s granted permissions lists to include all
   // permissions in the |extension|'s manifest and re-enables the
   // extension.
@@ -546,6 +558,9 @@
   // this extension initially.
   int GetDisableReasonsOnInstalled(const extensions::Extension* extension);
 
+  // Helper method to determine if an extension can be blocked.
+  bool CanBlockExtension(const extensions::Extension* extension) const;
+
   // Helper to determine if updating an extensions should proceed immediately,
   // or if we should delay the update until further notice.
   bool ShouldDelayExtensionUpdate(const std::string& extension_id,
@@ -556,10 +571,11 @@
   void ManageBlacklist(
       const extensions::Blacklist::BlacklistStateMap& blacklisted_ids);
 
-  // Add extensions in |blocked| to blacklisted_extensions, remove extensions
-  // that are neither in |blocked|, nor in |unchanged|.
-  void UpdateBlockedExtensions(const extensions::ExtensionIdSet& blocked,
-                               const extensions::ExtensionIdSet& unchanged);
+  // Add extensions in |blacklisted| to blacklisted_extensions, remove
+  // extensions that are neither in |blacklisted|, nor in |unchanged|.
+  void UpdateBlacklistedExtensions(
+      const extensions::ExtensionIdSet& to_blacklist,
+      const extensions::ExtensionIdSet& unchanged);
 
   void UpdateGreylistedExtensions(
       const extensions::ExtensionIdSet& greylist,
@@ -684,6 +700,9 @@
   // first time.
   bool is_first_run_;
 
+  // Set to true if extensions are all to be blocked.
+  bool block_extensions_;
+
   // Store the ids of reloading extensions. We use this to re-enable extensions
   // which were disabled for a reload.
   std::set<std::string> reloading_extensions_;
@@ -732,6 +751,8 @@
                            GreylistUnknownDontChange);
   FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
                            ManagementPolicyProhibitsEnableOnInstalled);
+  FRIEND_TEST_ALL_PREFIXES(ExtensionServiceTest,
+                           BlockAndUnblockBlacklistedExtension);
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionService);
 };
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 4c09b930..ae5b0dd 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -871,6 +871,39 @@
     service()->TrackTerminatedExtensionForTest(extension);
   }
 
+  testing::AssertionResult IsBlocked(const std::string& id) {
+    scoped_ptr<extensions::ExtensionSet> all_unblocked_extensions =
+        registry()->GenerateInstalledExtensionsSet(
+            ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::BLOCKED);
+    if (all_unblocked_extensions.get()->Contains(id))
+      return testing::AssertionFailure() << id << " is still unblocked!";
+    if (!registry()->blocked_extensions().Contains(id))
+      return testing::AssertionFailure() << id << " is not blocked!";
+    return testing::AssertionSuccess();
+  }
+
+  // Helper method to test that an extension moves through being blocked and
+  // unblocked as appropriate for its type.
+  void AssertExtensionBlocksAndUnblocks(
+      bool should_block, const std::string extension_id) {
+    // Assume we start in an unblocked state.
+    EXPECT_FALSE(IsBlocked(extension_id));
+
+    // Block the extensions.
+    service()->BlockAllExtensions();
+    base::RunLoop().RunUntilIdle();
+
+    if (should_block)
+      ASSERT_TRUE(IsBlocked(extension_id));
+    else
+      ASSERT_FALSE(IsBlocked(extension_id));
+
+    service()->UnblockAllExtensions();
+    base::RunLoop().RunUntilIdle();
+
+    ASSERT_FALSE(IsBlocked(extension_id));
+  }
+
   size_t GetPrefKeyCount() {
     const base::DictionaryValue* dict =
         profile()->GetPrefs()->GetDictionary("extensions.settings");
@@ -3585,6 +3618,157 @@
 
 #endif  // defined(ENABLE_BLACKLIST_TESTS)
 
+// Tests blocking then unblocking enabled extensions after the service has been
+// initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockEnabledExtension) {
+  InitializeGoodInstalledExtensionService();
+  service()->Init();
+
+  AssertExtensionBlocksAndUnblocks(true, good0);
+}
+
+// Tests blocking then unblocking disabled extensions after the service has been
+// initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockDisabledExtension) {
+  InitializeGoodInstalledExtensionService();
+  service()->Init();
+
+  service()->DisableExtension(good0, Extension::DISABLE_RELOAD);
+
+  AssertExtensionBlocksAndUnblocks(true, good0);
+}
+
+// Tests blocking then unblocking terminated extensions after the service has
+// been initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockTerminatedExtension) {
+  InitializeGoodInstalledExtensionService();
+  service()->Init();
+
+  TerminateExtension(good0);
+
+  AssertExtensionBlocksAndUnblocks(true, good0);
+}
+
+// Tests blocking then unblocking policy-forced extensions after the service has
+// been initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockPolicyExtension) {
+  InitializeEmptyExtensionServiceWithTestingPrefs();
+
+  {
+    ManagementPrefUpdater pref(profile_->GetTestingPrefService());
+    // // Blacklist everything.
+    // pref.SetBlacklistedByDefault(true);
+    // Mark good.crx for force-installation.
+    pref.SetIndividualExtensionAutoInstalled(
+        good_crx, "http://example.com/update_url", true);
+  }
+
+  // Have policy force-install an extension.
+  MockExtensionProvider* provider =
+      new MockExtensionProvider(service(), Manifest::EXTERNAL_POLICY_DOWNLOAD);
+  AddMockExternalProvider(provider);
+  provider->UpdateOrAddExtension(
+      good_crx, "1.0.0.0", data_dir().AppendASCII("good_crx"));
+
+  // Reloading extensions should find our externally registered extension
+  // and install it.
+  content::WindowedNotificationObserver observer(
+      extensions::NOTIFICATION_CRX_INSTALLER_DONE,
+      content::NotificationService::AllSources());
+  service()->CheckForExternalUpdates();
+  observer.Wait();
+
+  AssertExtensionBlocksAndUnblocks(false, good_crx);
+}
+
+
+#if defined(ENABLE_BLACKLIST_TESTS)
+// Tests blocking then unblocking extensions that are blacklisted both before
+// and after Init().
+TEST_F(ExtensionServiceTest, BlockAndUnblockBlacklistedExtension) {
+  extensions::TestBlacklist test_blacklist;
+
+  InitializeGoodInstalledExtensionService();
+  test_blacklist.Attach(service()->blacklist_);
+
+  test_blacklist.SetBlacklistState(
+      good0, extensions::BLACKLISTED_MALWARE, true);
+  base::RunLoop().RunUntilIdle();
+
+  service()->Init();
+
+  test_blacklist.SetBlacklistState(
+      good1, extensions::BLACKLISTED_MALWARE, true);
+  base::RunLoop().RunUntilIdle();
+
+  // Blacklisted extensions stay blacklisted.
+  AssertExtensionBlocksAndUnblocks(false, good0);
+  AssertExtensionBlocksAndUnblocks(false, good1);
+
+  service()->BlockAllExtensions();
+
+  // Remove an extension from the blacklist while the service is blocked.
+  test_blacklist.SetBlacklistState(
+      good0, extensions::NOT_BLACKLISTED, true);
+  // Add an extension to the blacklist while the service is blocked.
+  test_blacklist.SetBlacklistState(
+      good2, extensions::BLACKLISTED_MALWARE, true);
+  base::RunLoop().RunUntilIdle();
+
+  // Go directly to blocked, do not pass go, do not collect $200.
+  ASSERT_TRUE(IsBlocked(good0));
+  // Get on the blacklist - even if you were blocked!
+  ASSERT_FALSE(IsBlocked(good2));
+}
+#endif  // defined(ENABLE_BLACKLIST_TESTS)
+
+// Tests blocking then unblocking enabled component extensions after the service
+// has been initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockEnabledComponentExtension) {
+  InitializeEmptyExtensionServiceWithTestingPrefs();
+
+  // Install a component extension.
+  base::FilePath path = data_dir()
+                            .AppendASCII("good")
+                            .AppendASCII("Extensions")
+                            .AppendASCII(good0)
+                            .AppendASCII("1.0.0.0");
+  std::string manifest;
+  ASSERT_TRUE(base::ReadFileToString(
+      path.Append(extensions::kManifestFilename), &manifest));
+  service()->component_loader()->Add(manifest, path);
+  service()->Init();
+
+  // Component extension should never block.
+  AssertExtensionBlocksAndUnblocks(false, good0);
+}
+
+// Tests blocking then unblocking a theme after the service has been
+// initialized.
+TEST_F(ExtensionServiceTest, BlockAndUnblockTheme) {
+  InitializeEmptyExtensionService();
+  service()->Init();
+
+  base::FilePath path = data_dir().AppendASCII("theme.crx");
+  InstallCRX(path, INSTALL_NEW);
+
+  AssertExtensionBlocksAndUnblocks(true, theme_crx);
+}
+
+// Tests that blocking extensions before Init() results in loading blocked
+// extensions.
+TEST_F(ExtensionServiceTest, WillNotLoadExtensionsWhenBlocked) {
+  InitializeGoodInstalledExtensionService();
+
+  service()->BlockAllExtensions();
+
+  service()->Init();
+
+  ASSERT_TRUE(IsBlocked(good0));
+  ASSERT_TRUE(IsBlocked(good0));
+  ASSERT_TRUE(IsBlocked(good0));
+}
+
 // Will not install extension blacklisted by policy.
 TEST_F(ExtensionServiceTest, BlacklistedByPolicyWillNotInstall) {
   InitializeEmptyExtensionServiceWithTestingPrefs();
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 1eceba9..2def7686 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -993,6 +993,7 @@
 void ProfileManager::DoFinalInitForServices(Profile* profile,
                                             bool go_off_the_record) {
 #if defined(ENABLE_EXTENSIONS)
+  ProfileInfoCache& cache = GetProfileInfoCache();
   extensions::ExtensionSystem::Get(profile)->InitForRegularProfile(
       !go_off_the_record);
   // During tests, when |profile| is an instance of TestingProfile,
@@ -1001,6 +1002,16 @@
     extensions::ExtensionSystem::Get(profile)->extension_service()->
         RegisterContentSettings(profile->GetHostContentSettingsMap());
   }
+  // Set the block extensions bit on the ExtensionService. There likely are no
+  // blockable extensions to block.
+  size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
+  if (profile_index != std::string::npos &&
+      cache.ProfileIsSigninRequiredAtIndex(profile_index)) {
+    extensions::ExtensionSystem::Get(profile)
+        ->extension_service()
+        ->BlockAllExtensions();
+  }
+
 #endif
 #if defined(ENABLE_MANAGED_USERS) && !defined(OS_ANDROID)
   // Initialization needs to happen after extension system initialization (for
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 4cb2940..96310bd0 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -17,6 +17,8 @@
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/profile_chooser_constants.h"
@@ -29,6 +31,14 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/user_metrics.h"
 
+#if defined(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/extension_service.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_factory.h"
+#include "extensions/browser/extension_system.h"
+#endif  // defined(ENABLE_EXTENSIONS)
+
 #if !defined(OS_IOS)
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -45,6 +55,20 @@
 const char kNewProfileManagementExperimentInternalName[] =
     "enable-new-profile-management";
 
+#if defined(ENABLE_EXTENSIONS)
+void BlockExtensions(Profile* profile) {
+  ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  extension_service->BlockAllExtensions();
+}
+
+void UnblockExtensions(Profile* profile) {
+  ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  extension_service->UnblockAllExtensions();
+}
+#endif  // defined(ENABLE_EXTENSIONS)
+
 // Handles running a callback when a new Browser for the given profile
 // has been completely created.
 class BrowserAddedForProfileObserver : public chrome::BrowserListObserver {
@@ -98,6 +122,19 @@
     is_first_run = chrome::startup::IS_FIRST_RUN;
   }
 
+#if defined(ENABLE_EXTENSIONS)
+  // The signin bit will still be set if the profile is being unlocked and the
+  // browser window for it is opening. As part of this unlock process, unblock
+  // all the extensions.
+  const ProfileInfoCache& cache =
+      g_browser_process->profile_manager()->GetProfileInfoCache();
+  int index = cache.GetIndexOfProfileWithPath(profile->GetPath());
+  if (!profile->IsGuestSession() &&
+      cache.ProfileIsSigninRequiredAtIndex(index)) {
+    UnblockExtensions(profile);
+  }
+#endif  // defined(ENABLE_EXTENSIONS)
+
   // If |always_create| is false, and we have a |callback| to run, check
   // whether a browser already exists so that we can run the callback. We don't
   // want to rely on the observer listening to OnBrowserSetLastActive in this
@@ -297,11 +334,17 @@
 }
 
 void LockBrowserCloseSuccess(const base::FilePath& profile_path) {
-  ProfileInfoCache* cache =
-      &g_browser_process->profile_manager()->GetProfileInfoCache();
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  ProfileInfoCache* cache = &profile_manager->GetProfileInfoCache();
 
   cache->SetProfileSigninRequiredAtIndex(
       cache->GetIndexOfProfileWithPath(profile_path), true);
+
+#if defined(ENABLE_EXTENSIONS)
+  // Profile guaranteed to exist for it to have been locked.
+  BlockExtensions(profile_manager->GetProfileByPath(profile_path));
+#endif  // defined(ENABLE_EXTENSIONS)
+
   chrome::HideTaskManager();
   UserManager::Show(profile_path,
                     profiles::USER_MANAGER_NO_TUTORIAL,
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index cecfd459..758b5d97 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -300,6 +300,7 @@
     case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
       Details<const UnloadedExtensionInfo> unloaded_details(details);
       if (unloaded_details->reason != UnloadedExtensionInfo::REASON_UPDATE &&
+          unloaded_details->reason != UnloadedExtensionInfo::REASON_LOCK_ALL &&
           unloaded_details->extension->is_theme() &&
           unloaded_details->extension->id() == GetThemeID()) {
         UseDefaultTheme();
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index a7831ef..3502e0d 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -922,20 +922,11 @@
       "312745D9BF916161191143F6490085EEA0434997"   // Google Talk debug
     ]
   },
-  "screenlockPrivate": [{
-    "platforms": ["chromeos"],
+  "screenlockPrivate": {
     "channel": "stable",
     "extension_types": ["platform_app"],
     "location": "component"
-  }, {
-    "platforms": ["mac", "win", "linux"],
-    "channel": "stable",
-    "extension_types": ["platform_app"],
-    "whitelist": [
-      "lkegkdgachcnekllcdfkijonogckdnjo",  // API test
-      "E13990DC5440B6E270503DA27A35762F423725C3"   // dogfood
-    ]
-  }],
+  },
   "screensaver": {
     "channel": "stable",
     "extension_types": ["legacy_packaged_app", "hosted_app", "platform_app"]
diff --git a/extensions/browser/extension_registry.cc b/extensions/browser/extension_registry.cc
index 769539e9..62891d8 100644
--- a/extensions/browser/extension_registry.cc
+++ b/extensions/browser/extension_registry.cc
@@ -21,11 +21,22 @@
 
 scoped_ptr<ExtensionSet> ExtensionRegistry::GenerateInstalledExtensionsSet()
     const {
+  return GenerateInstalledExtensionsSet(EVERYTHING).Pass();
+}
+
+scoped_ptr<ExtensionSet> ExtensionRegistry::GenerateInstalledExtensionsSet(
+    int include_mask) const {
   scoped_ptr<ExtensionSet> installed_extensions(new ExtensionSet);
-  installed_extensions->InsertAll(enabled_extensions_);
-  installed_extensions->InsertAll(disabled_extensions_);
-  installed_extensions->InsertAll(terminated_extensions_);
-  installed_extensions->InsertAll(blacklisted_extensions_);
+  if (include_mask & IncludeFlag::ENABLED)
+    installed_extensions->InsertAll(enabled_extensions_);
+  if (include_mask & IncludeFlag::DISABLED)
+    installed_extensions->InsertAll(disabled_extensions_);
+  if (include_mask & IncludeFlag::TERMINATED)
+    installed_extensions->InsertAll(terminated_extensions_);
+  if (include_mask & IncludeFlag::BLACKLISTED)
+    installed_extensions->InsertAll(blacklisted_extensions_);
+  if (include_mask & IncludeFlag::BLOCKED)
+    installed_extensions->InsertAll(blocked_extensions_);
   return installed_extensions.Pass();
 }
 
@@ -108,6 +119,11 @@
     if (extension)
       return extension;
   }
+  if (include_mask & BLOCKED) {
+    const Extension* extension = blocked_extensions_.GetByID(lowercase_id);
+    if (extension)
+      return extension;
+  }
   return NULL;
 }
 
@@ -147,11 +163,21 @@
   return blacklisted_extensions_.Remove(id);
 }
 
+bool ExtensionRegistry::AddBlocked(
+    const scoped_refptr<const Extension>& extension) {
+  return blocked_extensions_.Insert(extension);
+}
+
+bool ExtensionRegistry::RemoveBlocked(const std::string& id) {
+  return blocked_extensions_.Remove(id);
+}
+
 void ExtensionRegistry::ClearAll() {
   enabled_extensions_.Clear();
   disabled_extensions_.Clear();
   terminated_extensions_.Clear();
   blacklisted_extensions_.Clear();
+  blocked_extensions_.Clear();
 }
 
 void ExtensionRegistry::SetDisabledModificationCallback(
diff --git a/extensions/browser/extension_registry.h b/extensions/browser/extension_registry.h
index 50f75c9..d29a37b 100644
--- a/extensions/browser/extension_registry.h
+++ b/extensions/browser/extension_registry.h
@@ -33,12 +33,13 @@
  public:
   // Flags to pass to GetExtensionById() to select which sets to look in.
   enum IncludeFlag {
-    NONE        = 0,
-    ENABLED     = 1 << 0,
-    DISABLED    = 1 << 1,
-    TERMINATED  = 1 << 2,
+    NONE = 0,
+    ENABLED = 1 << 0,
+    DISABLED = 1 << 1,
+    TERMINATED = 1 << 2,
     BLACKLISTED = 1 << 3,
-    EVERYTHING = (1 << 4) - 1,
+    BLOCKED = 1 << 4,
+    EVERYTHING = (1 << 5) - 1,
   };
 
   explicit ExtensionRegistry(content::BrowserContext* browser_context);
@@ -63,11 +64,21 @@
   const ExtensionSet& blacklisted_extensions() const {
     return blacklisted_extensions_;
   }
+  const ExtensionSet& blocked_extensions() const { return blocked_extensions_; }
 
-  // Returns a set of all installed, disabled, blacklisted, and terminated
-  // extensions.
+  // Returns the set of all installed extensions, regardless of state (enabled,
+  // disabled, etc). Equivalent to GenerateInstalledExtensionSet(EVERYTHING).
   scoped_ptr<ExtensionSet> GenerateInstalledExtensionsSet() const;
 
+  // Returns a set of all extensions in the subsets specified by |include_mask|.
+  //  * enabled_extensions()     --> ExtensionRegistry::ENABLED
+  //  * disabled_extensions()    --> ExtensionRegistry::DISABLED
+  //  * terminated_extensions()  --> ExtensionRegistry::TERMINATED
+  //  * blacklisted_extensions() --> ExtensionRegistry::BLACKLISTED
+  //  * blocked_extensions()     --> ExtensionRegistry::BLOCKED
+  scoped_ptr<ExtensionSet> GenerateInstalledExtensionsSet(
+      int include_mask) const;
+
   // The usual observer interface.
   void AddObserver(ExtensionRegistryObserver* observer);
   void RemoveObserver(ExtensionRegistryObserver* observer);
@@ -107,6 +118,7 @@
   //  * disabled_extensions()    --> ExtensionRegistry::DISABLED
   //  * terminated_extensions()  --> ExtensionRegistry::TERMINATED
   //  * blacklisted_extensions() --> ExtensionRegistry::BLACKLISTED
+  //  * blocked_extensions()     --> ExtensionRegistry::BLOCKED
   // Returns NULL if the extension is not found in the selected sets.
   const Extension* GetExtensionById(const std::string& id,
                                     int include_mask) const;
@@ -136,6 +148,10 @@
   bool AddBlacklisted(const scoped_refptr<const Extension>& extension);
   bool RemoveBlacklisted(const std::string& id);
 
+  // As above, but for the blocked set.
+  bool AddBlocked(const scoped_refptr<const Extension>& extension);
+  bool RemoveBlocked(const std::string& id);
+
   // Removes all extensions from all sets.
   void ClearAll();
 
@@ -164,6 +180,9 @@
   // un-blacklisted.
   ExtensionSet blacklisted_extensions_;
 
+  // Extensions that are installed and blocked. Will never be loaded.
+  ExtensionSet blocked_extensions_;
+
   ObserverList<ExtensionRegistryObserver> observers_;
 
   content::BrowserContext* const browser_context_;
diff --git a/extensions/common/extension.h b/extensions/common/extension.h
index 391c7e5..d845332 100644
--- a/extensions/common/extension.h
+++ b/extensions/common/extension.h
@@ -69,6 +69,7 @@
     // DEPRECATED: Special state for component extensions.
     // Maintained as a placeholder since states may be stored to disk.
     ENABLED_COMPONENT_DEPRECATED,
+    // Add new states here as this enum is stored in prefs.
     NUM_STATES
   };
 
@@ -541,6 +542,7 @@
     REASON_TERMINATE,         // Extension has terminated.
     REASON_BLACKLIST,         // Extension has been blacklisted.
     REASON_PROFILE_SHUTDOWN,  // Profile is being shut down.
+    REASON_LOCK_ALL,          // All extensions for the profile are blocked.
   };
 
   Reason reason;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 09c35fb..131479a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -44424,7 +44424,6 @@
   <int value="1024" label="CORRUPTED"/>
   <int value="2048" label="REMOTE_INSTALL"/>
   <int value="4096" label="INACTIVE_EPHEMERAL_APP"/>
-  <int value="8192" label="EXTERNAL_EXTENSION"/>
 </enum>
 
 <enum name="ExtensionFileWriteResult" type="int">