Refactor APIPermission to separate out specific permission registration.

Adds a delegate to the PermissionsInfo global to do the specific registration; adds a ScopedTestingPermissionsInfo for unit tests.
Preparation for moving permissions to top-level extensions.

Also adds a base class for unittests, ExtensionTest, to deal with manifest handler / permission setup.

BUG=162530,172712

Review URL: https://chromiumcodereview.appspot.com/13370002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192252 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 0f6b4c0..c86d034 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -72,6 +72,8 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_l10n_util.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "chrome/common/extensions/permissions/permissions_info.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/switch_utils.h"
 #include "chrome/common/url_constants.h"
@@ -187,8 +189,9 @@
   InitIdleMonitor();
 #endif
 
+  extensions::PermissionsInfo::GetInstance()->InitializeWithDelegate(
+      extensions::ChromeAPIPermissions());
   extension_event_router_forwarder_ = new extensions::EventRouterForwarder;
-
   ExtensionRendererState::GetInstance()->Init();
 
 #if defined(ENABLE_MESSAGE_CENTER)
diff --git a/chrome/browser/extensions/active_tab_unittest.cc b/chrome/browser/extensions/active_tab_unittest.cc
index de7dfa7..e3afb37c 100644
--- a/chrome/browser/extensions/active_tab_unittest.cc
+++ b/chrome/browser/extensions/active_tab_unittest.cc
@@ -16,6 +16,8 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/features/feature.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
 #include "chrome/common/extensions/value_builder.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "content/public/browser/browser_thread.h"
@@ -55,7 +57,8 @@
 class ActiveTabTest : public ChromeRenderViewHostTestHarness {
  public:
   ActiveTabTest()
-      : extension(CreateTestExtension("deadbeef", true)),
+      : permissions_info_(ChromeAPIPermissions()),
+        extension(CreateTestExtension("deadbeef", true)),
         another_extension(CreateTestExtension("feedbeef", true)),
         extension_without_active_tab(CreateTestExtension("badbeef", false)),
         ui_thread_(BrowserThread::UI, MessageLoop::current()) {}
@@ -110,6 +113,8 @@
     return extension->HasAPIPermissionForTab(tab_id, APIPermission::kTab);
   }
 
+  ScopedTestingPermissionsInfo permissions_info_;
+
   // An extension with the activeTab permission.
   scoped_refptr<const Extension> extension;
 
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
index 7169743..2cfc545 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
@@ -6,18 +6,17 @@
 #include "base/values.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
 #include "chrome/common/extensions/api/permissions.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "extensions/common/url_pattern_set.h"
 #include "googleurl/src/gurl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using extensions::api::permissions::Permissions;
 using extensions::permissions_api_helpers::PackPermissionSet;
 using extensions::permissions_api_helpers::UnpackPermissionSet;
-using extensions::api::permissions::Permissions;
-using extensions::APIPermission;
-using extensions::APIPermissionSet;
-using extensions::PermissionSet;
-using extensions::URLPatternSet;
+
+namespace extensions {
 
 namespace {
 
@@ -28,8 +27,11 @@
 
 }  // namespace
 
+class ExtensionPermissionsAPIHelpers : public ExtensionTest {
+};
+
 // Tests that we can convert PermissionSets to and from values.
-TEST(ExtensionPermissionsAPIHelpers, Pack) {
+TEST_F(ExtensionPermissionsAPIHelpers, Pack) {
   APIPermissionSet apis;
   apis.insert(APIPermission::kTab);
   apis.insert(APIPermission::kWebRequest);
@@ -78,7 +80,7 @@
 
 // Tests various error conditions and edge cases when unpacking values
 // into PermissionSets.
-TEST(ExtensionPermissionsAPIHelpers, Unpack) {
+TEST_F(ExtensionPermissionsAPIHelpers, Unpack) {
   scoped_ptr<ListValue> apis(new ListValue());
   apis->Append(Value::CreateStringValue("tabs"));
   scoped_ptr<ListValue> origins(new ListValue());
@@ -173,3 +175,5 @@
     EXPECT_EQ(error, "'unknown_permission' is not a recognized permission.");
   }
 }
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc b/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc
index d5727118..08f3986 100644
--- a/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc
+++ b/chrome/browser/extensions/api/storage/settings_frontend_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/extensions/api/storage/settings_test_util.h"
 #include "chrome/browser/value_store/value_store.h"
 #include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -49,14 +50,15 @@
 
 }
 
-class ExtensionSettingsFrontendTest : public testing::Test {
+class ExtensionSettingsFrontendTest : public ExtensionTest {
  public:
-   ExtensionSettingsFrontendTest()
+  ExtensionSettingsFrontendTest()
       : storage_factory_(new util::ScopedSettingsStorageFactory()),
         ui_thread_(BrowserThread::UI, MessageLoop::current()),
         file_thread_(BrowserThread::FILE, MessageLoop::current()) {}
 
   virtual void SetUp() OVERRIDE {
+    ExtensionTest::SetUp();
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     profile_.reset(new util::MockProfile(temp_dir_.path()));
     ResetFrontend();
@@ -67,6 +69,7 @@
     profile_.reset();
     // Execute any pending deletion tasks.
     message_loop_.RunUntilIdle();
+    ExtensionTest::TearDown();
   }
 
  protected:
diff --git a/chrome/browser/extensions/component_loader_unittest.cc b/chrome/browser/extensions/component_loader_unittest.cc
index 9b2b980..bedcfee1 100644
--- a/chrome/browser/extensions/component_loader_unittest.cc
+++ b/chrome/browser/extensions/component_loader_unittest.cc
@@ -14,8 +14,7 @@
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_set.h"
-#include "chrome/common/extensions/incognito_handler.h"
-#include "chrome/common/extensions/manifest_handler.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_pref_service_syncable.h"
 #include "components/user_prefs/pref_registry_syncable.h"
@@ -74,18 +73,16 @@
 
 }  // namespace
 
-class ComponentLoaderTest : public testing::Test {
+class ComponentLoaderTest : public ExtensionTest {
  public:
-  ComponentLoaderTest() :
+  ComponentLoaderTest()
       // Note: we pass the same pref service here, to stand in for both
       // user prefs and local state.
-      component_loader_(&extension_service_, &prefs_, &local_state_) {
+      : component_loader_(&extension_service_, &prefs_, &local_state_) {
   }
 
   virtual void SetUp() OVERRIDE {
-    (new BackgroundManifestHandler)->Register();
-    (new IncognitoHandler)->Register();
-
+    ExtensionTest::SetUp();
     extension_path_ =
         GetBasePath().AppendASCII("good")
                      .AppendASCII("Extensions")
@@ -114,11 +111,6 @@
 #endif
   }
 
-  virtual void TearDown() OVERRIDE {
-    ManifestHandler::ClearRegistryForTesting();
-    testing::Test::TearDown();
-  }
-
  protected:
   MockExtensionService extension_service_;
   TestingPrefServiceSyncable prefs_;
diff --git a/chrome/browser/extensions/convert_web_app_unittest.cc b/chrome/browser/extensions/convert_web_app_unittest.cc
index fbb5128..6ba83b6 100644
--- a/chrome/browser/extensions/convert_web_app_unittest.cc
+++ b/chrome/browser/extensions/convert_web_app_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/extensions/api/icons/icons_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_icon_set.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "chrome/common/web_apps.h"
 #include "extensions/common/extension_resource.h"
@@ -80,17 +81,12 @@
 
 }  // namespace
 
-class ExtensionFromWebApp : public ::testing::Test {
- public:
+class ExtensionFromWebApp : public ExtensionTest {
+ protected:
   virtual void SetUp() OVERRIDE {
-    testing::Test::SetUp();
+    ExtensionTest::SetUp();
     (new IconsHandler)->Register();
   }
-
-  virtual void TearDown() OVERRIDE {
-    ManifestHandler::ClearRegistryForTesting();
-    testing::Test::TearDown();
-  }
 };
 
 TEST_F(ExtensionFromWebApp, GenerateVersion) {
diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc
index d88f32a..8c6e5457 100644
--- a/chrome/browser/extensions/extension_info_map_unittest.cc
+++ b/chrome/browser/extensions/extension_info_map_unittest.cc
@@ -9,6 +9,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
@@ -25,7 +26,7 @@
 
 namespace {
 
-class ExtensionInfoMapTest : public testing::Test {
+class ExtensionInfoMapTest : public extensions::ExtensionTest {
  public:
   ExtensionInfoMapTest()
       : ui_thread_(BrowserThread::UI, &message_loop_),
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 37639c68..173f8f4 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -20,7 +20,6 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
-#include "chrome/common/extensions/permissions/permissions_info.h"
 #include "components/user_prefs/pref_registry_syncable.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
@@ -64,6 +63,7 @@
 void ExtensionPrefsTest::RegisterPreferences(PrefRegistrySyncable* registry) {}
 
 void ExtensionPrefsTest::SetUp() {
+  ExtensionTest::SetUp();
   RegisterPreferences(prefs_.pref_registry());
   Initialize();
 }
@@ -78,6 +78,7 @@
   Verify();
   prefs_.pref_service()->CommitPendingWrite();
   message_loop_.RunUntilIdle();
+  ExtensionTest::TearDown();
 }
 
 // Tests the LastPingDay/SetLastPingDay functions.
diff --git a/chrome/browser/extensions/extension_prefs_unittest.h b/chrome/browser/extensions/extension_prefs_unittest.h
index c199d19..86b622bf 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.h
+++ b/chrome/browser/extensions/extension_prefs_unittest.h
@@ -7,6 +7,7 @@
 
 #include "base/message_loop.h"
 #include "chrome/browser/extensions/test_extension_prefs.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,7 +21,7 @@
 class Extension;
 
 // Base class for extension preference-related unit tests.
-class ExtensionPrefsTest : public testing::Test {
+class ExtensionPrefsTest : public ExtensionTest {
  public:
   ExtensionPrefsTest();
   virtual ~ExtensionPrefsTest();
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index b1253eb..a6068b5 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -68,7 +68,6 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_l10n_util.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
-#include "chrome/common/extensions/manifest_handler.h"
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "chrome/common/extensions/manifest_handlers/requirements_handler.h"
 #include "chrome/common/extensions/manifest_url_handler.h"
@@ -548,7 +547,7 @@
 }
 
 void ExtensionServiceTestBase::SetUp() {
-  testing::Test::SetUp();
+  ExtensionTest::SetUp();
   ExtensionErrorReporter::GetInstance()->ClearErrors();
   (new extensions::BackgroundManifestHandler)->Register();
   (new extensions::ContentScriptsHandler)->Register();
@@ -557,10 +556,6 @@
   (new extensions::RequirementsHandler)->Register();
 }
 
-void ExtensionServiceTestBase::TearDown() {
-  extensions::ManifestHandler::ClearRegistryForTesting();
-}
-
 class ExtensionServiceTest
   : public ExtensionServiceTestBase, public content::NotificationObserver {
  public:
diff --git a/chrome/browser/extensions/extension_service_unittest.h b/chrome/browser/extensions/extension_service_unittest.h
index f5bd434..6f46f77 100644
--- a/chrome/browser/extensions/extension_service_unittest.h
+++ b/chrome/browser/extensions/extension_service_unittest.h
@@ -12,6 +12,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/feature_switch.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,7 +23,7 @@
 class ManagementPolicy;
 }
 
-class ExtensionServiceTestBase : public testing::Test {
+class ExtensionServiceTestBase : public extensions::ExtensionTest {
  public:
   ExtensionServiceTestBase();
   virtual ~ExtensionServiceTestBase();
@@ -48,8 +49,6 @@
 
   virtual void SetUp() OVERRIDE;
 
-  virtual void TearDown() OVERRIDE;
-
   void set_extensions_enabled(bool enabled) {
     service_->set_extensions_enabled(enabled);
   }
diff --git a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
index 62cd4f1..e61f8d04 100644
--- a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
+++ b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/common/content_settings_types.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/manifest.h"
 #include "chrome/common/extensions/manifest_handler.h"
 #include "chrome/test/base/testing_profile.h"
@@ -22,13 +23,13 @@
 
 namespace keys = extension_manifest_keys;
 
-class ExtensionSpecialStoragePolicyTest : public testing::Test {
- public:
+class ExtensionSpecialStoragePolicyTest : public extensions::ExtensionTest {
+ protected:
   virtual void SetUp() {
+    extensions::ExtensionTest::SetUp();
     policy_ = new ExtensionSpecialStoragePolicy(NULL);
   }
 
- protected:
   scoped_refptr<Extension> CreateProtectedApp() {
 #if defined(OS_WIN)
     base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc
index a623506..b91cc7f 100644
--- a/chrome/browser/extensions/extension_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_ui_unittest.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/manifest_handler.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread.h"
@@ -23,15 +23,15 @@
 using extensions::Extension;
 using extensions::Manifest;
 
-class ExtensionUITest : public testing::Test {
+class ExtensionUITest : public extensions::ExtensionTest {
  public:
   ExtensionUITest()
       : ui_thread_(content::BrowserThread::UI, &message_loop_),
-        file_thread_(content::BrowserThread::FILE, &message_loop_) {}
+        file_thread_(content::BrowserThread::FILE, &message_loop_)  {}
 
  protected:
   virtual void SetUp() OVERRIDE {
-    testing::Test::SetUp();
+    ExtensionTest::SetUp();
 
     // Create an ExtensionService and ManagementPolicy to inject into the
     // ExtensionSettingsHandler.
@@ -54,8 +54,7 @@
     profile_.reset();
     // Execute any pending deletion tasks.
     message_loop_.RunUntilIdle();
-    extensions::ManifestHandler::ClearRegistryForTesting();
-    testing::Test::TearDown();
+    ExtensionTest::TearDown();
   }
 
   static DictionaryValue* DeserializeJSONTestData(const base::FilePath& path,
diff --git a/chrome/browser/extensions/script_bubble_controller_unittest.cc b/chrome/browser/extensions/script_bubble_controller_unittest.cc
index 12bc49d..4843209 100644
--- a/chrome/browser/extensions/script_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/script_bubble_controller_unittest.cc
@@ -18,6 +18,8 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/feature_switch.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
 #include "chrome/common/extensions/value_builder.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -35,7 +37,8 @@
   ScriptBubbleControllerTest()
       : ui_thread_(BrowserThread::UI, MessageLoop::current()),
         file_thread_(BrowserThread::FILE, MessageLoop::current()),
-        enable_script_bubble_(FeatureSwitch::script_bubble(), true) {
+        enable_script_bubble_(FeatureSwitch::script_bubble(), true),
+        permissions_info_(ChromeAPIPermissions()) {
   }
 
   virtual void SetUp() OVERRIDE {
@@ -66,6 +69,7 @@
   content::TestBrowserThread ui_thread_;
   content::TestBrowserThread file_thread_;
   FeatureSwitch::ScopedOverride enable_script_bubble_;
+  ScopedTestingPermissionsInfo permissions_info_;
 };
 
 TEST_F(ScriptBubbleControllerTest, Basics) {
diff --git a/chrome/browser/extensions/startup_helper.cc b/chrome/browser/extensions/startup_helper.cc
index 42a51b4..5ef51ec 100644
--- a/chrome/browser/extensions/startup_helper.cc
+++ b/chrome/browser/extensions/startup_helper.cc
@@ -22,6 +22,7 @@
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/incognito_handler.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "ipc/ipc_message.h"
@@ -39,6 +40,8 @@
 namespace extensions {
 
 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
+  PermissionsInfo::GetInstance()->InitializeWithDelegate(
+      ChromeAPIPermissions());
   (new DefaultLocaleHandler)->Register();
   (new BackgroundManifestHandler)->Register();
   (new IncognitoHandler)->Register();
diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc
index b4466464..06d46f49 100644
--- a/chrome/browser/extensions/user_script_listener_unittest.cc
+++ b/chrome/browser/extensions/user_script_listener_unittest.cc
@@ -151,6 +151,7 @@
   virtual void TearDown() OVERRIDE {
     listener_ = NULL;
     MessageLoop::current()->RunUntilIdle();
+    ExtensionServiceTestBase::TearDown();
   }
 
  protected:
diff --git a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
index 3ea9db9..2ea255d 100644
--- a/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
+++ b/chrome/browser/media_galleries/media_file_system_registry_unittest.cc
@@ -34,6 +34,8 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/incognito_handler.h"
 #include "chrome/common/extensions/manifest_handler.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -372,6 +374,7 @@
   // Needed for extension service & friends to work.
   content::TestBrowserThread ui_thread_;
   content::TestBrowserThread file_thread_;
+  extensions::ScopedTestingPermissionsInfo permissions_info_;
 
 // TODO(gbillock): Eliminate windows-specific code from this test.
 #if defined(OS_WIN)
@@ -574,7 +577,8 @@
 
 MediaFileSystemRegistryTest::MediaFileSystemRegistryTest()
     : ui_thread_(content::BrowserThread::UI, MessageLoop::current()),
-      file_thread_(content::BrowserThread::FILE, MessageLoop::current()) {
+      file_thread_(content::BrowserThread::FILE, MessageLoop::current()),
+      permissions_info_(extensions::ChromeAPIPermissions()) {
 }
 
 void MediaFileSystemRegistryTest::CreateProfileState(size_t profile_count) {
diff --git a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
index 5a3a4ff..8f010b0 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
@@ -22,8 +22,7 @@
 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/incognito_handler.h"
-#include "chrome/common/extensions/manifest_handler.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread.h"
 #include "sync/api/string_ordinal.h"
@@ -59,7 +58,7 @@
 
 }  // namespace
 
-class MediaGalleriesPreferencesTest : public testing::Test {
+class MediaGalleriesPreferencesTest : public extensions::ExtensionTest {
  public:
   typedef std::map<std::string /*device id*/, MediaGalleryPrefIdSet>
       DeviceIdPrefIdsMap;
@@ -79,15 +78,13 @@
   }
 
   virtual void SetUp() OVERRIDE {
-    testing::Test::SetUp();
+    extensions::ExtensionTest::SetUp();
 
     extensions::TestExtensionSystem* extension_system(
         static_cast<extensions::TestExtensionSystem*>(
             extensions::ExtensionSystem::Get(profile_.get())));
     extension_system->CreateExtensionService(
         CommandLine::ForCurrentProcess(), base::FilePath(), false);
-    (new extensions::BackgroundManifestHandler)->Register();
-    (new extensions::IncognitoHandler)->Register();
 
     gallery_prefs_.reset(new MediaGalleriesPreferences(profile_.get()));
 
@@ -121,8 +118,7 @@
 
   virtual void TearDown() OVERRIDE {
     Verify();
-    extensions::ManifestHandler::ClearRegistryForTesting();
-    testing::Test::TearDown();
+    extensions::ExtensionTest::TearDown();
   }
 
   void Verify() {
diff --git a/chrome/browser/ui/cocoa/location_bar/action_box_menu_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/location_bar/action_box_menu_bubble_controller_unittest.mm
index f24ca1b..158480e 100644
--- a/chrome/browser/ui/cocoa/location_bar/action_box_menu_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/location_bar/action_box_menu_bubble_controller_unittest.mm
@@ -16,6 +16,8 @@
 #include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/extensions/incognito_handler.h"
 #include "chrome/common/extensions/manifest_handler.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -47,6 +49,10 @@
 
 class ActionBoxMenuBubbleControllerTest : public CocoaProfileTest {
  public:
+  ActionBoxMenuBubbleControllerTest()
+      : permissions_info_(extensions::ChromeAPIPermissions()) {
+  }
+
   virtual void SetUp() OVERRIDE {
     CocoaProfileTest::SetUp();
     ASSERT_TRUE(browser());
@@ -121,9 +127,10 @@
     ASSERT_TRUE(found);
   }
 
- public:
+ protected:
   ActionBoxMenuBubbleController* controller_;
   MenuDelegate menu_delegate_;
+  extensions::ScopedTestingPermissionsInfo permissions_info_;
   ExtensionService* service_;
 };
 
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index a0b0eb9..9e0744d 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -278,6 +278,8 @@
         'common/extensions/permissions/bluetooth_device_permission.h',
         'common/extensions/permissions/bluetooth_device_permission_data.cc',
         'common/extensions/permissions/bluetooth_device_permission_data.h',
+        'common/extensions/permissions/chrome_api_permissions.cc',
+        'common/extensions/permissions/chrome_api_permissions.h',
         'common/extensions/permissions/media_galleries_permission.cc',
         'common/extensions/permissions/media_galleries_permission.h',
         'common/extensions/permissions/media_galleries_permission_data.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 1f92e10..0529d097 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -186,6 +186,8 @@
         'common/extensions/extension_builder.h',
         'common/extensions/extension_test_util.cc',
         'common/extensions/extension_test_util.h',
+        'common/extensions/permissions/scoped_testing_permissions_info.cc',
+        'common/extensions/permissions/scoped_testing_permissions_info.h',
         'common/extensions/value_builder.cc',
         'common/extensions/value_builder.h',
         'renderer/chrome_mock_render_thread.cc',
@@ -1508,7 +1510,9 @@
         'common/extensions/extension_l10n_util_unittest.cc',
         'common/extensions/extension_localization_peer_unittest.cc',
         'common/extensions/extension_set_unittest.cc',
+        'common/extensions/extension_sync_type_unittest.cc',
         'common/extensions/extension_unittest.cc',
+        'common/extensions/extension_unittest.h',
         'common/extensions/feature_switch_unittest.cc',
         'common/extensions/features/base_feature_provider_unittest.cc',
         'common/extensions/features/complex_feature_unittest.cc',
@@ -1822,11 +1826,10 @@
             ['exclude', '^browser/sync/glue/chrome_extensions_activity_monitor_unittest.cc'],
             ['exclude', '^common/extensions/api/'],
           ],
-          'sources!':[
+          'sources!': [
             'browser/extensions/extension_context_menu_model_unittest.cc',
             'browser/extensions/extension_ui_unittest.cc',
             'browser/extensions/permissions_updater_unittest.cc',
-            'common/extensions/extension_unittest.cc',
           ],
         }],
         ['use_ash==1', {
diff --git a/chrome/common/extensions/api/commands/commands_manifest_unittest.cc b/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
index f6eb432..c642ebc 100644
--- a/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
+++ b/chrome/common/extensions/api/commands/commands_manifest_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/commands/commands_handler.h"
+#include "chrome/common/extensions/api/extension_action/browser_action_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace errors = extension_manifest_errors;
@@ -84,4 +85,16 @@
       errors::kInvalidKeyBinding);
 }
 
+TEST_F(CommandsManifestTest, BrowserActionSynthesizesCommand) {
+  (new BrowserActionHandler)->Register();
+  scoped_refptr<Extension> extension =
+      LoadAndExpectSuccess("browser_action_synthesizes_command.json");
+  // An extension with a browser action but no extension command specified
+  // should get a command assigned to it.
+  const extensions::Command* command =
+      CommandsInfo::GetBrowserActionCommand(extension);
+  ASSERT_TRUE(command != NULL);
+  ASSERT_EQ(ui::VKEY_UNKNOWN, command->accelerator().key_code());
+}
+
 }  // namespace extensions
diff --git a/chrome/common/extensions/api/extension_api_unittest.cc b/chrome/common/extensions/api/extension_api_unittest.cc
index 72db57d4..98830ea 100644
--- a/chrome/common/extensions/api/extension_api_unittest.cc
+++ b/chrome/common/extensions/api/extension_api_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/values.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/features/api_feature.h"
 #include "chrome/common/extensions/features/base_feature_provider.h"
 #include "chrome/common/extensions/features/simple_feature.h"
@@ -31,7 +32,10 @@
   return new APIFeature();
 }
 
-TEST(ExtensionAPI, Creation) {
+class ExtensionAPITest : public ExtensionTest {
+};
+
+TEST_F(ExtensionAPITest, Creation) {
   ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance();
   EXPECT_EQ(shared_instance, ExtensionAPI::GetSharedInstance());
 
@@ -58,7 +62,7 @@
   }
 }
 
-TEST(ExtensionAPI, SplitDependencyName) {
+TEST_F(ExtensionAPITest, SplitDependencyName) {
   struct {
     std::string input;
     std::string expected_feature_type;
@@ -82,7 +86,7 @@
   }
 }
 
-TEST(ExtensionAPI, IsPrivileged) {
+TEST_F(ExtensionAPITest, IsPrivileged) {
   scoped_ptr<ExtensionAPI> extension_api(
       ExtensionAPI::CreateWithDefaultConfiguration());
 
@@ -111,7 +115,7 @@
   EXPECT_FALSE(extension_api->IsPrivileged("storage.set"));
 }
 
-TEST(ExtensionAPI, IsPrivilegedFeatures) {
+TEST_F(ExtensionAPITest, IsPrivilegedFeatures) {
   struct {
     std::string api_full_name;
     bool expect_is_privilged;
@@ -217,7 +221,7 @@
   }
 }
 
-TEST(ExtensionAPI, LazyGetSchema) {
+TEST_F(ExtensionAPITest, LazyGetSchema) {
   scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
 
   EXPECT_EQ(NULL, apis->GetSchema(""));
@@ -271,7 +275,7 @@
   return CreateExtensionWithPermissions(permissions);
 }
 
-TEST(ExtensionAPI, ExtensionWithUnprivilegedAPIs) {
+TEST_F(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
   scoped_refptr<Extension> extension;
   {
     std::set<std::string> permissions;
@@ -326,7 +330,7 @@
                                           GURL()).is_available());
 }
 
-TEST(ExtensionAPI, ExtensionWithDependencies) {
+TEST_F(ExtensionAPITest, ExtensionWithDependencies) {
   // Extension with the "ttsEngine" permission but not the "tts" permission; it
   // should not automatically get "tts" permission.
   {
@@ -368,7 +372,7 @@
       api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available();
 }
 
-TEST(ExtensionAPI, URLMatching) {
+TEST_F(ExtensionAPITest, URLMatching) {
   scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
 
   // "app" API is available to all URLs that content scripts can be injected.
@@ -394,7 +398,7 @@
                           "chrome-extension://fakeextension"));
 }
 
-TEST(ExtensionAPI, GetAPINameFromFullName) {
+TEST_F(ExtensionAPITest, GetAPINameFromFullName) {
   struct {
     std::string input;
     std::string api_name;
@@ -422,7 +426,7 @@
   }
 }
 
-TEST(ExtensionAPI, DefaultConfigurationFeatures) {
+TEST_F(ExtensionAPITest, DefaultConfigurationFeatures) {
   scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
 
   SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
@@ -456,7 +460,7 @@
   }
 }
 
-TEST(ExtensionAPI, FeaturesRequireContexts) {
+TEST_F(ExtensionAPITest, FeaturesRequireContexts) {
   // TODO(cduvall): Make this check API featues.
   scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue());
   scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue());
@@ -494,7 +498,7 @@
   EXPECT_TRUE(list->GetDictionary(list_index, out));
 }
 
-TEST(ExtensionAPI, TypesHaveNamespace) {
+TEST_F(ExtensionAPITest, TypesHaveNamespace) {
   base::FilePath manifest_path;
   PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
   manifest_path = manifest_path.AppendASCII("extensions")
diff --git a/chrome/common/extensions/extension_sync_type_unittest.cc b/chrome/common/extensions/extension_sync_type_unittest.cc
new file mode 100644
index 0000000..3256c8d8
--- /dev/null
+++ b/chrome/common/extensions/extension_sync_type_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
+#include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/extension_unittest.h"
+#include "chrome/common/extensions/manifest.h"
+#include "chrome/common/extensions/manifest_url_handler.h"
+#include "googleurl/src/gurl.h"
+
+namespace keys = extension_manifest_keys;
+namespace errors = extension_manifest_errors;
+
+namespace extensions {
+
+class ExtensionSyncTypeTest : public ExtensionTest {
+ protected:
+  virtual void SetUp() OVERRIDE {
+    ExtensionTest::SetUp();
+    (new UpdateURLHandler)->Register();
+    (new PluginsHandler)->Register();
+  }
+
+  enum SyncTestExtensionType {
+    EXTENSION,
+    APP,
+    USER_SCRIPT,
+    THEME
+  };
+
+  static scoped_refptr<Extension> MakeSyncTestExtension(
+      SyncTestExtensionType type,
+      const GURL& update_url,
+      const GURL& launch_url,
+      Manifest::Location location,
+      int num_plugins,
+      const base::FilePath& extension_path,
+      int creation_flags) {
+    DictionaryValue source;
+    source.SetString(keys::kName, "PossiblySyncableExtension");
+    source.SetString(keys::kVersion, "0.0.0.0");
+    if (type == APP)
+      source.SetString(keys::kApp, "true");
+    if (type == THEME)
+      source.Set(keys::kTheme, new DictionaryValue());
+    if (!update_url.is_empty()) {
+      source.SetString(keys::kUpdateURL, update_url.spec());
+    }
+    if (!launch_url.is_empty()) {
+      source.SetString(keys::kLaunchWebURL, launch_url.spec());
+    }
+    if (type != THEME) {
+      source.SetBoolean(keys::kConvertedFromUserScript, type == USER_SCRIPT);
+      ListValue* plugins = new ListValue();
+      for (int i = 0; i < num_plugins; ++i) {
+        DictionaryValue* plugin = new DictionaryValue();
+        plugin->SetString(keys::kPluginsPath, "");
+        plugins->Set(i, plugin);
+      }
+      source.Set(keys::kPlugins, plugins);
+    }
+
+    std::string error;
+    scoped_refptr<Extension> extension = Extension::Create(
+        extension_path, location, source, creation_flags, &error);
+    EXPECT_TRUE(extension);
+    EXPECT_EQ("", error);
+    return extension;
+  }
+
+  static const char kValidUpdateUrl1[];
+  static const char kValidUpdateUrl2[];
+};
+
+const char ExtensionSyncTypeTest::kValidUpdateUrl1[] =
+    "http://clients2.google.com/service/update2/crx";
+const char ExtensionSyncTypeTest::kValidUpdateUrl2[] =
+    "https://clients2.google.com/service/update2/crx";
+
+TEST_F(ExtensionSyncTypeTest, NormalExtensionNoUpdateUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, GetSyncTypeUserScriptValidUpdateUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(USER_SCRIPT, GURL(kValidUpdateUrl1), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, UserScriptNoUpdateUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(USER_SCRIPT, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, ThemeNoUpdateUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(THEME, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, ExtensionWithLaunchUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL("http://www.google.com"),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, ExtensionExternal) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::EXTERNAL_PREF, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+
+  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, UserScriptThirdPartyUpdateUrl) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(
+          USER_SCRIPT, GURL("http://third-party.update_url.com"), GURL(),
+          Manifest::INTERNAL, 0, base::FilePath(), Extension::NO_FLAGS));
+  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, OnlyDisplayAppsInLauncher) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+
+  EXPECT_FALSE(extension->ShouldDisplayInAppLauncher());
+  EXPECT_FALSE(extension->ShouldDisplayInNewTabPage());
+
+  scoped_refptr<Extension> app(
+      MakeSyncTestExtension(APP, GURL(), GURL("http://www.google.com"),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
+  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
+}
+
+TEST_F(ExtensionSyncTypeTest, DisplayInXManifestProperties) {
+  DictionaryValue manifest;
+  manifest.SetString(keys::kName, "TestComponentApp");
+  manifest.SetString(keys::kVersion, "0.0.0.0");
+  manifest.SetString(keys::kApp, "true");
+  manifest.SetString(keys::kPlatformAppBackgroundPage, "");
+
+  std::string error;
+  scoped_refptr<Extension> app;
+
+  // Default to true.
+  app = Extension::Create(
+      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
+  EXPECT_EQ(error, std::string());
+  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
+  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
+
+  // Value display_in_NTP defaults to display_in_launcher.
+  manifest.SetBoolean(keys::kDisplayInLauncher, false);
+  app = Extension::Create(
+      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
+  EXPECT_EQ(error, std::string());
+  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
+  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
+
+  // Value display_in_NTP = true overriding display_in_launcher = false.
+  manifest.SetBoolean(keys::kDisplayInNewTabPage, true);
+  app = Extension::Create(
+      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
+  EXPECT_EQ(error, std::string());
+  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
+  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
+
+  // Value display_in_NTP = false only, overrides default = true.
+  manifest.Remove(keys::kDisplayInLauncher, NULL);
+  manifest.SetBoolean(keys::kDisplayInNewTabPage, false);
+  app = Extension::Create(
+      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
+  EXPECT_EQ(error, std::string());
+  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
+  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
+
+  // Error checking.
+  manifest.SetString(keys::kDisplayInNewTabPage, "invalid");
+  app = Extension::Create(
+      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
+  EXPECT_EQ(error, std::string(errors::kInvalidDisplayInNewTabPage));
+}
+
+TEST_F(ExtensionSyncTypeTest, OnlySyncInternal) {
+  scoped_refptr<Extension> extension_internal(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_TRUE(extension_internal->IsSyncable());
+
+  scoped_refptr<Extension> extension_noninternal(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::COMPONENT, 0, base::FilePath(),
+                            Extension::NO_FLAGS));
+  EXPECT_FALSE(extension_noninternal->IsSyncable());
+}
+
+TEST_F(ExtensionSyncTypeTest, DontSyncDefault) {
+  scoped_refptr<Extension> extension_default(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 0, base::FilePath(),
+                            Extension::WAS_INSTALLED_BY_DEFAULT));
+  EXPECT_FALSE(extension_default->IsSyncable());
+}
+
+// These last 2 tests don't make sense on Chrome OS, where extension plugins
+// are not allowed.
+#if !defined(OS_CHROMEOS)
+TEST_F(ExtensionSyncTypeTest, ExtensionWithPlugin) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 1, base::FilePath(),
+                            Extension::NO_FLAGS));
+  if (extension)
+    EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+
+TEST_F(ExtensionSyncTypeTest, ExtensionWithTwoPlugins) {
+  scoped_refptr<Extension> extension(
+      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
+                            Manifest::INTERNAL, 2, base::FilePath(),
+                            Extension::NO_FLAGS));
+  if (extension)
+    EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
+}
+#endif // !defined(OS_CHROMEOS)
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index b1cda14..6a3c80d 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_unittest.h"
 
 #include "base/command_line.h"
 #include "base/file_util.h"
@@ -15,10 +15,10 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/api/commands/commands_handler.h"
 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/command.h"
+#include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_file_util.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
 #include "chrome/common/extensions/features/feature.h"
@@ -27,7 +27,9 @@
 #include "chrome/common/extensions/manifest_handler.h"
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "chrome/common/extensions/permissions/api_permission.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
 #include "chrome/common/extensions/permissions/socket_permission.h"
 #include "chrome/common/extensions/permissions/usb_device_permission.h"
 #include "chrome/common/url_constants.h"
@@ -105,21 +107,21 @@
 
 }  // namespace
 
-class ExtensionTest : public testing::Test {
- protected:
-  virtual void SetUp() OVERRIDE {
-    testing::Test::SetUp();
-    (new BackgroundManifestHandler)->Register();
-    (new CommandsHandler)->Register();
-    (new ContentScriptsHandler)->Register();
-    (new PluginsHandler)->Register();
-    (new IncognitoHandler)->Register();
-  }
+ExtensionTest::ExtensionTest() : permissions_info_(ChromeAPIPermissions()) {}
 
-  virtual void TearDown() OVERRIDE {
-    ManifestHandler::ClearRegistryForTesting();
-  }
-};
+void ExtensionTest::SetUp() {
+  testing::Test::SetUp();
+  (new BackgroundManifestHandler)->Register();
+  // We need the IncognitoHandler registered for all tests, since
+  // Extension uses it in Extension::CheckPlatformAppFeatures() as part of the
+  // creation process.
+  (new IncognitoHandler)->Register();
+}
+
+void ExtensionTest::TearDown() {
+  ManifestHandler::ClearRegistryForTesting();
+  testing::Test::TearDown();
+}
 
 // We persist location values in the preferences, so this is a sanity test that
 // someone doesn't accidentally change them.
@@ -245,6 +247,7 @@
 }
 
 TEST_F(ExtensionTest, EffectiveHostPermissions) {
+  (new ContentScriptsHandler)->Register();
   scoped_refptr<Extension> extension;
   URLPatternSet hosts;
 
@@ -419,6 +422,7 @@
 }
 
 TEST_F(ExtensionTest, GetPermissionMessages_ManyHosts) {
+  (new ContentScriptsHandler)->Register();
   scoped_refptr<Extension> extension;
   extension = LoadManifest("permissions", "many-hosts.json");
   std::vector<string16> warnings = extension->GetPermissionMessageStrings();
@@ -428,6 +432,7 @@
 }
 
 TEST_F(ExtensionTest, GetPermissionMessages_Plugins) {
+  (new PluginsHandler)->Register();
   scoped_refptr<Extension> extension;
   extension = LoadManifest("permissions", "plugins.json");
   std::vector<string16> warnings = extension->GetPermissionMessageStrings();
@@ -443,6 +448,7 @@
 }
 
 TEST_F(ExtensionTest, WantsFileAccess) {
+  (new ContentScriptsHandler)->Register();
   scoped_refptr<Extension> extension;
   GURL file_url("file:///etc/passwd");
 
@@ -550,19 +556,6 @@
   EXPECT_FALSE(extension->from_webstore());
 }
 
-TEST_F(ExtensionTest, BrowserActionSynthesizesCommand) {
-  scoped_refptr<Extension> extension;
-
-  extension = LoadManifest("api_test/browser_action/synthesized",
-                           "manifest.json");
-  // An extension with a browser action but no extension command specified
-  // should get a command assigned to it.
-  const extensions::Command* command =
-      CommandsInfo::GetBrowserActionCommand(extension);
-  ASSERT_TRUE(command != NULL);
-  ASSERT_EQ(ui::VKEY_UNKNOWN, command->accelerator().key_code());
-}
-
 // Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage
 // methods of Extension for extensions with various permissions.
 class ExtensionScriptAndCaptureVisibleTest : public testing::Test {
@@ -929,210 +922,6 @@
   EXPECT_TRUE(AllowedExclusivelyOnTab(extension, no_urls, 2));
 }
 
-namespace {
-enum SyncTestExtensionType {
-  EXTENSION,
-  APP,
-  USER_SCRIPT,
-  THEME
-};
-
-static scoped_refptr<Extension> MakeSyncTestExtension(
-    SyncTestExtensionType type,
-    const GURL& update_url,
-    const GURL& launch_url,
-    Manifest::Location location,
-    int num_plugins,
-    const base::FilePath& extension_path,
-    int creation_flags) {
-  DictionaryValue source;
-  source.SetString(extension_manifest_keys::kName,
-                   "PossiblySyncableExtension");
-  source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
-  if (type == APP)
-    source.SetString(extension_manifest_keys::kApp, "true");
-  if (type == THEME)
-    source.Set(extension_manifest_keys::kTheme, new DictionaryValue());
-  if (!update_url.is_empty()) {
-    source.SetString(extension_manifest_keys::kUpdateURL,
-                     update_url.spec());
-  }
-  if (!launch_url.is_empty()) {
-    source.SetString(extension_manifest_keys::kLaunchWebURL,
-                     launch_url.spec());
-  }
-  if (type != THEME) {
-    source.SetBoolean(extension_manifest_keys::kConvertedFromUserScript,
-                      type == USER_SCRIPT);
-    ListValue* plugins = new ListValue();
-    for (int i = 0; i < num_plugins; ++i) {
-      DictionaryValue* plugin = new DictionaryValue();
-      plugin->SetString(extension_manifest_keys::kPluginsPath, "");
-      plugins->Set(i, plugin);
-    }
-    source.Set(extension_manifest_keys::kPlugins, plugins);
-  }
-
-  std::string error;
-  scoped_refptr<Extension> extension = Extension::Create(
-      extension_path, location, source, creation_flags, &error);
-  EXPECT_TRUE(extension);
-  EXPECT_EQ("", error);
-  return extension;
-}
-
-static const char kValidUpdateUrl1[] =
-    "http://clients2.google.com/service/update2/crx";
-static const char kValidUpdateUrl2[] =
-    "https://clients2.google.com/service/update2/crx";
-}
-
-TEST_F(ExtensionTest, GetSyncTypeNormalExtensionNoUpdateUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-// http://crbug.com/172712
-TEST_F(ExtensionTest, DISABLED_GetSyncTypeUserScriptValidUpdateUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(USER_SCRIPT, GURL(kValidUpdateUrl1), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeUserScriptNoUpdateUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(USER_SCRIPT, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeThemeNoUpdateUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(THEME, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeExtensionWithLaunchUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL("http://www.google.com"),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_NE(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeExtensionExternal) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::EXTERNAL_PREF, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-
-  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeUserScriptThirdPartyUpdateUrl) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(
-          USER_SCRIPT, GURL("http://third-party.update_url.com"), GURL(),
-          Manifest::INTERNAL, 0, base::FilePath(), Extension::NO_FLAGS));
-  EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, OnlyDisplayAppsInLauncher) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-
-  EXPECT_FALSE(extension->ShouldDisplayInAppLauncher());
-  EXPECT_FALSE(extension->ShouldDisplayInNewTabPage());
-
-  scoped_refptr<Extension> app(
-      MakeSyncTestExtension(APP, GURL(), GURL("http://www.google.com"),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
-  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
-}
-
-TEST_F(ExtensionTest, DisplayInXManifestProperties) {
-  DictionaryValue manifest;
-  manifest.SetString(keys::kName, "TestComponentApp");
-  manifest.SetString(keys::kVersion, "0.0.0.0");
-  manifest.SetString(keys::kApp, "true");
-  manifest.SetString(keys::kPlatformAppBackgroundPage, "");
-
-  std::string error;
-  scoped_refptr<Extension> app;
-
-  // Default to true.
-  app = Extension::Create(
-      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
-  EXPECT_EQ(error, std::string());
-  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
-  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
-
-  // Value display_in_NTP defaults to display_in_launcher.
-  manifest.SetBoolean(keys::kDisplayInLauncher, false);
-  app = Extension::Create(
-      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
-  EXPECT_EQ(error, std::string());
-  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
-  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
-
-  // Value display_in_NTP = true overriding display_in_launcher = false.
-  manifest.SetBoolean(keys::kDisplayInNewTabPage, true);
-  app = Extension::Create(
-      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
-  EXPECT_EQ(error, std::string());
-  EXPECT_FALSE(app->ShouldDisplayInAppLauncher());
-  EXPECT_TRUE(app->ShouldDisplayInNewTabPage());
-
-  // Value display_in_NTP = false only, overrides default = true.
-  manifest.Remove(keys::kDisplayInLauncher, NULL);
-  manifest.SetBoolean(keys::kDisplayInNewTabPage, false);
-  app = Extension::Create(
-      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
-  EXPECT_EQ(error, std::string());
-  EXPECT_TRUE(app->ShouldDisplayInAppLauncher());
-  EXPECT_FALSE(app->ShouldDisplayInNewTabPage());
-
-  // Error checking.
-  manifest.SetString(keys::kDisplayInNewTabPage, "invalid");
-  app = Extension::Create(
-      base::FilePath(), Manifest::COMPONENT, manifest, 0, &error);
-  EXPECT_EQ(error, std::string(errors::kInvalidDisplayInNewTabPage));
-}
-
-TEST_F(ExtensionTest, OnlySyncInternal) {
-  scoped_refptr<Extension> extension_internal(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_TRUE(extension_internal->IsSyncable());
-
-  scoped_refptr<Extension> extension_noninternal(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::COMPONENT, 0, base::FilePath(),
-                            Extension::NO_FLAGS));
-  EXPECT_FALSE(extension_noninternal->IsSyncable());
-}
-
-TEST_F(ExtensionTest, DontSyncDefault) {
-  scoped_refptr<Extension> extension_default(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 0, base::FilePath(),
-                            Extension::WAS_INSTALLED_BY_DEFAULT));
-  EXPECT_FALSE(extension_default->IsSyncable());
-}
-
 TEST_F(ExtensionTest, OptionalOnlyPermission) {
   // Set feature current channel to dev because the only permission that must
   // be optional (usbDevices) is only available on dev channel.
@@ -1158,26 +947,4 @@
   EXPECT_TRUE(error.empty());
 }
 
-// These last 2 tests don't make sense on Chrome OS, where extension plugins
-// are not allowed.
-#if !defined(OS_CHROMEOS)
-TEST_F(ExtensionTest, GetSyncTypeExtensionWithPlugin) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 1, base::FilePath(),
-                            Extension::NO_FLAGS));
-  if (extension)
-    EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-
-TEST_F(ExtensionTest, GetSyncTypeExtensionWithTwoPlugins) {
-  scoped_refptr<Extension> extension(
-      MakeSyncTestExtension(EXTENSION, GURL(), GURL(),
-                            Manifest::INTERNAL, 2, base::FilePath(),
-                            Extension::NO_FLAGS));
-  if (extension)
-    EXPECT_EQ(extension->GetSyncType(), Extension::SYNC_TYPE_NONE);
-}
-#endif // !defined(OS_CHROMEOS)
-
 }  // namespace extensions
diff --git a/chrome/common/extensions/extension_unittest.h b/chrome/common/extensions/extension_unittest.h
new file mode 100644
index 0000000..99a75093
--- /dev/null
+++ b/chrome/common/extensions/extension_unittest.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_UNITTEST_H_
+#define CHROME_COMMON_EXTENSIONS_EXTENSION_UNITTEST_H_
+
+#include "base/compiler_specific.h"
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+// A base class for extension unit tests that sets up permissions and
+// universally useful manifest handlers.
+class ExtensionTest : public testing::Test {
+ public:
+  ExtensionTest();
+
+ protected:
+  virtual void SetUp() OVERRIDE;
+
+  virtual void TearDown() OVERRIDE;
+
+  ScopedTestingPermissionsInfo permissions_info_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_EXTENSION_UNITTEST_H_
diff --git a/chrome/common/extensions/features/base_feature_provider_unittest.cc b/chrome/common/extensions/features/base_feature_provider_unittest.cc
index df876dc..ec6f208 100644
--- a/chrome/common/extensions/features/base_feature_provider_unittest.cc
+++ b/chrome/common/extensions/features/base_feature_provider_unittest.cc
@@ -4,21 +4,19 @@
 
 #include "chrome/common/extensions/features/base_feature_provider.h"
 
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/features/permission_feature.h"
 #include "chrome/common/extensions/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using chrome::VersionInfo;
-using extensions::BaseFeatureProvider;
-using extensions::DictionaryBuilder;
-using extensions::Extension;
-using extensions::Feature;
-using extensions::ListBuilder;
-using extensions::Manifest;
-using extensions::PermissionFeature;
-using extensions::SimpleFeature;
 
-TEST(BaseFeatureProvider, ManifestFeatures) {
+namespace extensions {
+
+class BaseFeatureProviderTest : public ExtensionTest {
+};
+
+TEST_F(BaseFeatureProviderTest, ManifestFeatures) {
   BaseFeatureProvider* provider =
       BaseFeatureProvider::GetManifestFeatures();
   SimpleFeature* feature =
@@ -60,7 +58,7 @@
       extension.get(), Feature::UNSPECIFIED_CONTEXT).result());
 }
 
-TEST(BaseFeatureProvider, PermissionFeatures) {
+TEST_F(BaseFeatureProviderTest, PermissionFeatures) {
   BaseFeatureProvider* provider =
       BaseFeatureProvider::GetPermissionFeatures();
   SimpleFeature* feature =
@@ -106,7 +104,7 @@
   return new PermissionFeature();
 }
 
-TEST(BaseFeatureProvider, Validation) {
+TEST_F(BaseFeatureProviderTest, Validation) {
   scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
 
   base::DictionaryValue* feature1 = new base::DictionaryValue();
@@ -141,7 +139,7 @@
   EXPECT_TRUE(provider->GetFeature("feature2"));
 }
 
-TEST(BaseFeatureProvider, ComplexFeatures) {
+TEST_F(BaseFeatureProviderTest, ComplexFeatures) {
   scoped_ptr<base::DictionaryValue> rule(
       DictionaryBuilder()
       .Set("feature1",
@@ -189,3 +187,5 @@
         Feature::UNSPECIFIED_PLATFORM).result());
   }
 }
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/manifest_tests/extension_manifest_test.cc b/chrome/common/extensions/manifest_tests/extension_manifest_test.cc
index feb8c0b..b61d5f7 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifest_test.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifest_test.cc
@@ -11,9 +11,6 @@
 #include "base/values.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/extension_l10n_util.h"
-#include "chrome/common/extensions/extension_manifest_constants.h"
-#include "chrome/common/extensions/incognito_handler.h"
-#include "chrome/common/extensions/manifest_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 
 using extensions::Extension;
@@ -58,18 +55,6 @@
       // UNKNOWN == trunk.
       current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {}
 
-void ExtensionManifestTest::SetUp() {
-  testing::Test::SetUp();
-  // We need the IncognitoHandler registered for all tests, since
-  // Extension uses it in Extension::CheckPlatformAppFeatures() as part of the
-  // creation process.
-  (new extensions::IncognitoHandler)->Register();
-}
-
-void ExtensionManifestTest::TearDown() {
-  extensions::ManifestHandler::ClearRegistryForTesting();
-}
-
 // Helper class that simplifies creating methods that take either a filename
 // to a manifest or the manifest itself.
 ExtensionManifestTest::Manifest::Manifest(const char* name)
diff --git a/chrome/common/extensions/manifest_tests/extension_manifest_test.h b/chrome/common/extensions/manifest_tests/extension_manifest_test.h
index dbebb7b..2a063e4 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifest_test.h
+++ b/chrome/common/extensions/manifest_tests/extension_manifest_test.h
@@ -10,18 +10,16 @@
 #include "base/values.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/features/feature.h"
 #include "chrome/common/extensions/manifest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-class ExtensionManifestTest : public testing::Test {
+class ExtensionManifestTest : public extensions::ExtensionTest {
  public:
   ExtensionManifestTest();
 
  protected:
-  virtual void SetUp() OVERRIDE;
-  virtual void TearDown() OVERRIDE;
-
   // Helper class that simplifies creating methods that take either a filename
   // to a manifest or the manifest itself.
   class Manifest {
diff --git a/chrome/common/extensions/permissions/api_permission.cc b/chrome/common/extensions/permissions/api_permission.cc
index 9e8ac087..2943649 100644
--- a/chrome/common/extensions/permissions/api_permission.cc
+++ b/chrome/common/extensions/permissions/api_permission.cc
@@ -4,12 +4,6 @@
 
 #include "chrome/common/extensions/permissions/api_permission.h"
 
-#include "chrome/common/extensions/permissions/bluetooth_device_permission.h"
-#include "chrome/common/extensions/permissions/media_galleries_permission.h"
-#include "chrome/common/extensions/permissions/permissions_info.h"
-#include "chrome/common/extensions/permissions/socket_permission.h"
-#include "chrome/common/extensions/permissions/usb_device_permission.h"
-#include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
@@ -19,9 +13,6 @@
 using extensions::PermissionMessage;
 using extensions::PermissionMessages;
 
-const char kOldUnlimitedStoragePermission[] = "unlimited_storage";
-const char kWindowsPermission[] = "windows";
-
 class SimpleAPIPermission : public APIPermission {
  public:
   explicit SimpleAPIPermission(const APIPermissionInfo* permission)
@@ -95,11 +86,6 @@
   virtual void Log(std::string* log) const OVERRIDE { }
 };
 
-template<typename T>
-APIPermission* CreateAPIPermission(const APIPermissionInfo* permission) {
-  return new T(permission);
-}
-
 }  // namespace
 
 namespace extensions {
@@ -158,221 +144,4 @@
       message_id_, l10n_util::GetStringUTF16(l10n_message_id_));
 }
 
-// static
-void APIPermissionInfo::RegisterAllPermissions(
-    PermissionsInfo* info) {
-
-  struct PermissionRegistration {
-    APIPermission::ID id;
-    const char* name;
-    int flags;
-    int l10n_message_id;
-    PermissionMessage::ID message_id;
-    APIPermissionConstructor constructor;
-  } PermissionsToRegister[] = {
-    // Register permissions for all extension types.
-    { APIPermission::kBackground, "background" },
-    { APIPermission::kClipboardRead, "clipboardRead", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD,
-      PermissionMessage::kClipboard },
-    { APIPermission::kClipboardWrite, "clipboardWrite" },
-    { APIPermission::kDeclarativeContent, "declarativeContent" },
-    { APIPermission::kDeclarativeWebRequest, "declarativeWebRequest" },
-    { APIPermission::kDownloads, "downloads", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_DOWNLOADS,
-      PermissionMessage::kDownloads },
-    { APIPermission::kExperimental, "experimental", kFlagCannotBeOptional },
-    { APIPermission::kGeolocation, "geolocation", kFlagCannotBeOptional,
-      IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION,
-      PermissionMessage::kGeolocation },
-    { APIPermission::kNotification, "notifications" },
-    { APIPermission::kScreensaver, "screensaver" },
-    { APIPermission::kUnlimitedStorage, "unlimitedStorage",
-      kFlagCannotBeOptional },
-
-    // Register extension permissions.
-    { APIPermission::kActiveTab, "activeTab" },
-    { APIPermission::kAdView, "adview" },
-    { APIPermission::kAlarms, "alarms" },
-    { APIPermission::kBookmark, "bookmarks", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS,
-      PermissionMessage::kBookmarks },
-    { APIPermission::kBrowsingData, "browsingData" },
-    { APIPermission::kContentSettings, "contentSettings", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS,
-      PermissionMessage::kContentSettings },
-    { APIPermission::kContextMenus, "contextMenus" },
-    { APIPermission::kCookie, "cookies" },
-    { APIPermission::kFileBrowserHandler, "fileBrowserHandler",
-      kFlagCannotBeOptional },
-    { APIPermission::kFontSettings, "fontSettings", kFlagCannotBeOptional },
-    { APIPermission::kHistory, "history", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
-      PermissionMessage::kBrowsingHistory },
-    { APIPermission::kIdle, "idle" },
-    { APIPermission::kInput, "input", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_INPUT,
-      PermissionMessage::kInput },
-    { APIPermission::kManagement, "management", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT,
-      PermissionMessage::kManagement },
-    { APIPermission::kNativeMessaging, "nativeMessaging" },
-    { APIPermission::kPower, "power", },
-    { APIPermission::kPrivacy, "privacy", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_PRIVACY,
-      PermissionMessage::kPrivacy },
-    { APIPermission::kSessionRestore, "sessionRestore" },
-    { APIPermission::kStorage, "storage" },
-    { APIPermission::kSyncFileSystem, "syncFileSystem", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_SYNCFILESYSTEM,
-      PermissionMessage::kSyncFileSystem },
-    { APIPermission::kTab, "tabs", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_TABS,
-      PermissionMessage::kTabs },
-    { APIPermission::kTopSites, "topSites", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
-      PermissionMessage::kBrowsingHistory },
-    { APIPermission::kTts, "tts", 0, kFlagCannotBeOptional },
-    { APIPermission::kTtsEngine, "ttsEngine", kFlagCannotBeOptional,
-      IDS_EXTENSION_PROMPT_WARNING_TTS_ENGINE,
-      PermissionMessage::kTtsEngine },
-    { APIPermission::kWebNavigation, "webNavigation", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_TABS, PermissionMessage::kTabs },
-    { APIPermission::kWebRequest, "webRequest" },
-    { APIPermission::kWebRequestBlocking, "webRequestBlocking" },
-    { APIPermission::kWebView, "webview", kFlagCannotBeOptional },
-
-    // Register private permissions.
-    { APIPermission::kAutoTestPrivate, "autotestPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kBookmarkManagerPrivate, "bookmarkManagerPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kChromeosInfoPrivate, "chromeosInfoPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kDeveloperPrivate, "developerPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kDial, "dial", kFlagCannotBeOptional },
-    { APIPermission::kDownloadsInternal, "downloadsInternal" },
-    { APIPermission::kFileBrowserHandlerInternal, "fileBrowserHandlerInternal",
-      kFlagCannotBeOptional },
-    { APIPermission::kFileBrowserPrivate, "fileBrowserPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kNetworkingPrivate, "networkingPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kManagedModePrivate, "managedModePrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kMediaPlayerPrivate, "mediaPlayerPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kMetricsPrivate, "metricsPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kSystemPrivate, "systemPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kCloudPrintPrivate, "cloudPrintPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kInputMethodPrivate, "inputMethodPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kEchoPrivate, "echoPrivate", kFlagCannotBeOptional },
-    { APIPermission::kRtcPrivate, "rtcPrivate", kFlagCannotBeOptional },
-    { APIPermission::kTerminalPrivate, "terminalPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kWallpaperPrivate, "wallpaperPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kWebRequestInternal, "webRequestInternal" },
-    { APIPermission::kWebSocketProxyPrivate, "webSocketProxyPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kWebstorePrivate, "webstorePrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kMediaGalleriesPrivate, "mediaGalleriesPrivate",
-      kFlagCannotBeOptional },
-    { APIPermission::kStreamsPrivate, "streamsPrivate", kFlagCannotBeOptional },
-
-    // Full url access permissions.
-    { APIPermission::kDebugger, "debugger",
-      kFlagImpliesFullURLAccess | kFlagCannotBeOptional,
-      IDS_EXTENSION_PROMPT_WARNING_DEBUGGER,
-      PermissionMessage::kDebugger },
-    { APIPermission::kDevtools, "devtools",
-      kFlagImpliesFullURLAccess | kFlagCannotBeOptional },
-    { APIPermission::kPageCapture, "pageCapture",
-      kFlagImpliesFullURLAccess },
-    { APIPermission::kTabCapture, "tabCapture",
-      kFlagImpliesFullURLAccess },
-    { APIPermission::kPlugin, "plugin",
-      kFlagImpliesFullURLAccess | kFlagImpliesFullAccess |
-          kFlagCannotBeOptional,
-      IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS,
-      PermissionMessage::kFullAccess },
-    { APIPermission::kProxy, "proxy",
-      kFlagImpliesFullURLAccess | kFlagCannotBeOptional },
-
-    // Platform-app permissions.
-    { APIPermission::kSerial, "serial", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_SERIAL,
-      PermissionMessage::kSerial },
-    // Because warning messages for the "socket" permission vary based on the
-    // permissions parameters, no message ID or message text is specified here.
-    // The message ID and text used will be determined at run-time in the
-    // |SocketPermission| class.
-    { APIPermission::kSocket, "socket", kFlagCannotBeOptional, 0,
-      PermissionMessage::kNone, &::CreateAPIPermission<SocketPermission> },
-    { APIPermission::kAppCurrentWindowInternal, "app.currentWindowInternal" },
-    { APIPermission::kAppRuntime, "app.runtime" },
-    { APIPermission::kAppWindow, "app.window" },
-    { APIPermission::kAudioCapture, "audioCapture", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_AUDIO_CAPTURE,
-      PermissionMessage::kAudioCapture },
-    { APIPermission::kVideoCapture, "videoCapture", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_VIDEO_CAPTURE,
-      PermissionMessage::kVideoCapture },
-    // The permission string for "fileSystem" is only shown when "write" is
-    // present. Read-only access is only granted after the user has been shown
-    // a file chooser dialog and selected a file. Selecting the file is
-    // considered consent to read it.
-    { APIPermission::kFileSystem, "fileSystem" },
-    { APIPermission::kFileSystemWrite, "fileSystem.write", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_FILE_SYSTEM_WRITE,
-      PermissionMessage::kFileSystemWrite },
-    // Because warning messages for the "mediaGalleries" permission vary based
-    // on the permissions parameters, no message ID or message text is
-    // specified here.
-    // The message ID and text used will be determined at run-time in the
-    // |MediaGalleriesPermission| class.
-    { APIPermission::kMediaGalleries, "mediaGalleries", kFlagNone, 0,
-      PermissionMessage::kNone,
-      &::CreateAPIPermission<MediaGalleriesPermission> },
-    { APIPermission::kPushMessaging, "pushMessaging", kFlagCannotBeOptional },
-    { APIPermission::kBluetooth, "bluetooth", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH,
-      PermissionMessage::kBluetooth },
-    { APIPermission::kBluetoothDevice, "bluetoothDevices",
-      kFlagNone, 0, PermissionMessage::kNone,
-      &::CreateAPIPermission<BluetoothDevicePermission> },
-    { APIPermission::kUsb, "usb", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_USB,
-      PermissionMessage::kUsb },
-    { APIPermission::kUsbDevice, "usbDevices",
-      kFlagMustBeOptional, 0, PermissionMessage::kNone,
-      &::CreateAPIPermission<UsbDevicePermission> },
-    { APIPermission::kSystemIndicator, "systemIndicator", kFlagNone,
-      IDS_EXTENSION_PROMPT_WARNING_SYSTEM_INDICATOR,
-      PermissionMessage::kSystemIndicator },
-    { APIPermission::kSystemInfoDisplay, "systemInfo.display" },
-    { APIPermission::kPointerLock, "pointerLock" },
-    { APIPermission::kFullscreen, "fullscreen" },
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(PermissionsToRegister); ++i) {
-    const PermissionRegistration& pr = PermissionsToRegister[i];
-    info->RegisterPermission(
-        pr.id, pr.name, pr.l10n_message_id,
-        pr.message_id ? pr.message_id : PermissionMessage::kNone,
-        pr.flags,
-        pr.constructor);
-  }
-
-  // Register aliases.
-  info->RegisterAlias("unlimitedStorage", kOldUnlimitedStoragePermission);
-  info->RegisterAlias("tabs", kWindowsPermission);
-}
-
 }  // namespace extensions
diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h
index a1e8582..f9719f3 100644
--- a/chrome/common/extensions/permissions/api_permission.h
+++ b/chrome/common/extensions/permissions/api_permission.h
@@ -12,12 +12,9 @@
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/pickle.h"
+#include "base/values.h"
 #include "chrome/common/extensions/permissions/permission_message.h"
 
-namespace base {
-class Value;
-}
-
 namespace IPC {
 class Message;
 }
@@ -25,7 +22,7 @@
 namespace extensions {
 
 class APIPermissionInfo;
-class PermissionsInfo;
+class ChromeAPIPermissions;
 
 // APIPermission is for handling some complex permissions. Please refer to
 // extensions::SocketPermission as an example.
@@ -269,8 +266,9 @@
   }
 
  private:
-  // Instances should only be constructed from within PermissionsInfo.
-  friend class PermissionsInfo;
+  // Instances should only be constructed from within a
+  // PermissionsInfo::Delegate.
+  friend class ChromeAPIPermissions;
   // Implementations of APIPermission will want to get the permission message,
   // but this class's implementation should be hidden from everyone else.
   friend class APIPermission;
@@ -283,9 +281,6 @@
       int flags,
       APIPermissionConstructor api_permission_constructor);
 
-  // Register ALL the permissions!
-  static void RegisterAllPermissions(PermissionsInfo* info);
-
   // Returns the localized permission message associated with this api.
   // Use GetMessage_ to avoid name conflict with macro GetMessage on Windows.
   PermissionMessage GetMessage_() const;
diff --git a/chrome/common/extensions/permissions/api_permission_set.cc b/chrome/common/extensions/permissions/api_permission_set.cc
index 62d76ae..3934b90 100644
--- a/chrome/common/extensions/permissions/api_permission_set.cc
+++ b/chrome/common/extensions/permissions/api_permission_set.cc
@@ -15,11 +15,11 @@
 
 namespace {
 
-using extensions::PermissionsInfo;
 using extensions::APIPermission;
 using extensions::APIPermissionInfo;
 using extensions::APIPermissionSet;
 using extensions::ErrorUtils;
+using extensions::PermissionsInfo;
 
 bool CreateAPIPermission(
     const std::string& permission_str,
@@ -27,9 +27,9 @@
     APIPermissionSet* api_permissions,
     string16* error,
     std::vector<std::string>* unhandled_permissions) {
-  PermissionsInfo* info = PermissionsInfo::GetInstance();
 
-  const APIPermissionInfo* permission_info = info->GetByName(permission_str);
+  const APIPermissionInfo* permission_info =
+      PermissionsInfo::GetInstance()->GetByName(permission_str);
   if (permission_info) {
     scoped_ptr<APIPermission> permission(
         permission_info->CreateAPIPermission());
@@ -55,10 +55,10 @@
 }
 
 bool ParseChildPermissions(const std::string& base_name,
-                          const Value* permission_value,
-                          APIPermissionSet* api_permissions,
-                          string16* error,
-                          std::vector<std::string>* unhandled_permissions) {
+                           const Value* permission_value,
+                           APIPermissionSet* api_permissions,
+                           string16* error,
+                           std::vector<std::string>* unhandled_permissions) {
   if (permission_value) {
     const ListValue* permissions;
     if (!permission_value->GetAsList(&permissions)) {
@@ -88,7 +88,7 @@
       }
 
       if (!CreateAPIPermission(base_name + '.' + permission_str, NULL,
-          api_permissions, error, unhandled_permissions))
+                               api_permissions, error, unhandled_permissions))
         return false;
     }
   }
@@ -147,12 +147,11 @@
 
 void APIPermissionSet::insert(APIPermission::ID id) {
   const APIPermissionInfo* permission_info =
-    PermissionsInfo::GetInstance()->GetByID(id);
+      PermissionsInfo::GetInstance()->GetByID(id);
   insert(permission_info->CreateAPIPermission());
 }
 
-void APIPermissionSet::insert(
-    APIPermission* permission) {
+void APIPermissionSet::insert(APIPermission* permission) {
   map_[permission->id()].reset(permission);
 }
 
@@ -285,7 +284,6 @@
     APIPermissionSet* api_permissions,
     string16* error,
     std::vector<std::string>* unhandled_permissions) {
-  PermissionsInfo* info = PermissionsInfo::GetInstance();
   for (size_t i = 0; i < permissions->GetSize(); ++i) {
     std::string permission_str;
     const base::Value* permission_value = NULL;
@@ -308,7 +306,7 @@
 
     // Check if this permission is a special case where its value should
     // be treated as a list of child permissions.
-    if (info->HasChildPermissions(permission_str)) {
+    if (PermissionsInfo::GetInstance()->HasChildPermissions(permission_str)) {
       if (!ParseChildPermissions(permission_str, permission_value,
                                  api_permissions, error, unhandled_permissions))
         return false;
@@ -316,7 +314,7 @@
     }
 
     if (!CreateAPIPermission(permission_str, permission_value,
-        api_permissions, error, unhandled_permissions))
+                             api_permissions, error, unhandled_permissions))
       return false;
   }
   return true;
diff --git a/chrome/common/extensions/permissions/api_permission_set_unittest.cc b/chrome/common/extensions/permissions/api_permission_set_unittest.cc
index 182e5f8..1b8f6c7 100644
--- a/chrome/common/extensions/permissions/api_permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/api_permission_set_unittest.cc
@@ -5,17 +5,17 @@
 #include "base/pickle.h"
 #include "base/values.h"
 #include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/permissions/api_permission_set.h"
-#include "chrome/common/extensions/permissions/permissions_info.h"
 #include "ipc/ipc_message.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
 
-class APIPermissionSetTest : public testing::Test {
+class APIPermissionSetTest : public ExtensionTest {
 };
 
-TEST(APIPermissionSetTest, General) {
+TEST_F(APIPermissionSetTest, General) {
   APIPermissionSet apis;
   apis.insert(APIPermission::kTab);
   apis.insert(APIPermission::kBackground);
@@ -35,7 +35,7 @@
   EXPECT_EQ(apis.size(), 4u);
 }
 
-TEST(APIPermissionSetTest, CreateUnion) {
+TEST_F(APIPermissionSetTest, CreateUnion) {
   APIPermission* permission = NULL;
 
   APIPermissionSet apis1;
@@ -123,7 +123,7 @@
   EXPECT_EQ(expected_apis, result);
 }
 
-TEST(APIPermissionSetTest, CreateIntersection) {
+TEST_F(APIPermissionSetTest, CreateIntersection) {
   APIPermission* permission = NULL;
 
   APIPermissionSet apis1;
@@ -201,7 +201,7 @@
   EXPECT_EQ(expected_apis, result);
 }
 
-TEST(APIPermissionSetTest, CreateDifference) {
+TEST_F(APIPermissionSetTest, CreateDifference) {
   APIPermission* permission = NULL;
 
   APIPermissionSet apis1;
@@ -272,7 +272,7 @@
   EXPECT_TRUE(result2.empty());
 }
 
-TEST(APIPermissionSetTest, IPC) {
+TEST_F(APIPermissionSetTest, IPC) {
   APIPermission* permission = NULL;
 
   APIPermissionSet apis;
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
new file mode 100644
index 0000000..531fc79
--- /dev/null
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -0,0 +1,279 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
+
+#include "chrome/common/extensions/permissions/api_permission.h"
+#include "chrome/common/extensions/permissions/api_permission_set.h"
+#include "chrome/common/extensions/permissions/bluetooth_device_permission.h"
+#include "chrome/common/extensions/permissions/media_galleries_permission.h"
+#include "chrome/common/extensions/permissions/permission_message.h"
+#include "chrome/common/extensions/permissions/permissions_info.h"
+#include "chrome/common/extensions/permissions/socket_permission.h"
+#include "chrome/common/extensions/permissions/usb_device_permission.h"
+#include "grit/generated_resources.h"
+
+namespace extensions {
+
+namespace {
+
+const char kOldUnlimitedStoragePermission[] = "unlimited_storage";
+const char kWindowsPermission[] = "windows";
+
+template<typename T> APIPermission* CreateAPIPermission(
+    const APIPermissionInfo* permission) {
+  return new T(permission);
+}
+
+}  // namespace
+
+std::vector<APIPermissionInfo*> ChromeAPIPermissions::GetAllPermissions()
+    const {
+  struct PermissionRegistration {
+    APIPermission::ID id;
+    const char* name;
+    int flags;
+    int l10n_message_id;
+    PermissionMessage::ID message_id;
+    APIPermissionInfo::APIPermissionConstructor constructor;
+  } PermissionsToRegister[] = {
+    // Register permissions for all extension types.
+    { APIPermission::kBackground, "background" },
+    { APIPermission::kClipboardRead, "clipboardRead",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD,
+      PermissionMessage::kClipboard },
+    { APIPermission::kClipboardWrite, "clipboardWrite" },
+    { APIPermission::kDeclarativeContent, "declarativeContent" },
+    { APIPermission::kDeclarativeWebRequest, "declarativeWebRequest" },
+    { APIPermission::kDownloads, "downloads", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_DOWNLOADS,
+      PermissionMessage::kDownloads },
+    { APIPermission::kExperimental, "experimental",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kGeolocation, "geolocation",
+      APIPermissionInfo::kFlagCannotBeOptional,
+      IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION,
+      PermissionMessage::kGeolocation },
+    { APIPermission::kNotification, "notifications" },
+    { APIPermission::kScreensaver, "screensaver" },
+    { APIPermission::kUnlimitedStorage, "unlimitedStorage",
+      APIPermissionInfo::kFlagCannotBeOptional },
+
+    // Register extension permissions.
+    { APIPermission::kActiveTab, "activeTab" },
+    { APIPermission::kAdView, "adview" },
+    { APIPermission::kAlarms, "alarms" },
+    { APIPermission::kBookmark, "bookmarks", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS,
+      PermissionMessage::kBookmarks },
+    { APIPermission::kBrowsingData, "browsingData" },
+    { APIPermission::kContentSettings, "contentSettings",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS,
+      PermissionMessage::kContentSettings },
+    { APIPermission::kContextMenus, "contextMenus" },
+    { APIPermission::kCookie, "cookies" },
+    { APIPermission::kFileBrowserHandler, "fileBrowserHandler",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kFontSettings, "fontSettings",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kHistory, "history", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
+      PermissionMessage::kBrowsingHistory },
+    { APIPermission::kIdle, "idle" },
+    { APIPermission::kInput, "input", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_INPUT,
+      PermissionMessage::kInput },
+    { APIPermission::kManagement, "management", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT,
+      PermissionMessage::kManagement },
+    { APIPermission::kNativeMessaging, "nativeMessaging" },
+    { APIPermission::kPower, "power", },
+    { APIPermission::kPrivacy, "privacy", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_PRIVACY,
+      PermissionMessage::kPrivacy },
+    { APIPermission::kSessionRestore, "sessionRestore" },
+    { APIPermission::kStorage, "storage" },
+    { APIPermission::kSyncFileSystem, "syncFileSystem",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_SYNCFILESYSTEM,
+      PermissionMessage::kSyncFileSystem },
+    { APIPermission::kTab, "tabs", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_TABS,
+      PermissionMessage::kTabs },
+    { APIPermission::kTopSites, "topSites", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
+      PermissionMessage::kBrowsingHistory },
+    { APIPermission::kTts, "tts", 0, APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kTtsEngine, "ttsEngine",
+      APIPermissionInfo::kFlagCannotBeOptional,
+      IDS_EXTENSION_PROMPT_WARNING_TTS_ENGINE,
+      PermissionMessage::kTtsEngine },
+    { APIPermission::kWebNavigation, "webNavigation",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_TABS, PermissionMessage::kTabs },
+    { APIPermission::kWebRequest, "webRequest" },
+    { APIPermission::kWebRequestBlocking, "webRequestBlocking" },
+    { APIPermission::kWebView, "webview",
+      APIPermissionInfo::kFlagCannotBeOptional },
+
+    // Register private permissions.
+    { APIPermission::kAutoTestPrivate, "autotestPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kBookmarkManagerPrivate, "bookmarkManagerPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kChromeosInfoPrivate, "chromeosInfoPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kDeveloperPrivate, "developerPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kDial, "dial", APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kDownloadsInternal, "downloadsInternal" },
+    { APIPermission::kFileBrowserHandlerInternal, "fileBrowserHandlerInternal",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kFileBrowserPrivate, "fileBrowserPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kNetworkingPrivate, "networkingPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kManagedModePrivate, "managedModePrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kMediaPlayerPrivate, "mediaPlayerPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kMetricsPrivate, "metricsPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kSystemPrivate, "systemPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kCloudPrintPrivate, "cloudPrintPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kInputMethodPrivate, "inputMethodPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kEchoPrivate, "echoPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kRtcPrivate, "rtcPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kTerminalPrivate, "terminalPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kWallpaperPrivate, "wallpaperPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kWebRequestInternal, "webRequestInternal" },
+    { APIPermission::kWebSocketProxyPrivate, "webSocketProxyPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kWebstorePrivate, "webstorePrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kMediaGalleriesPrivate, "mediaGalleriesPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kStreamsPrivate, "streamsPrivate",
+      APIPermissionInfo::kFlagCannotBeOptional },
+
+    // Full url access permissions.
+    { APIPermission::kDebugger, "debugger",
+      APIPermissionInfo::kFlagImpliesFullURLAccess |
+          APIPermissionInfo::kFlagCannotBeOptional,
+      IDS_EXTENSION_PROMPT_WARNING_DEBUGGER,
+      PermissionMessage::kDebugger },
+    { APIPermission::kDevtools, "devtools",
+      APIPermissionInfo::kFlagImpliesFullURLAccess |
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kPageCapture, "pageCapture",
+      APIPermissionInfo::kFlagImpliesFullURLAccess },
+    { APIPermission::kTabCapture, "tabCapture",
+      APIPermissionInfo::kFlagImpliesFullURLAccess },
+    { APIPermission::kPlugin, "plugin",
+      APIPermissionInfo::kFlagImpliesFullURLAccess |
+          APIPermissionInfo::kFlagImpliesFullAccess |
+          APIPermissionInfo::kFlagCannotBeOptional,
+      IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS,
+      PermissionMessage::kFullAccess },
+    { APIPermission::kProxy, "proxy",
+      APIPermissionInfo::kFlagImpliesFullURLAccess |
+          APIPermissionInfo::kFlagCannotBeOptional },
+
+    // Platform-app permissions.
+    { APIPermission::kSerial, "serial", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_SERIAL,
+      PermissionMessage::kSerial },
+    // Because warning messages for the "socket" permission vary based on the
+    // permissions parameters, no message ID or message text is specified here.
+    // The message ID and text used will be determined at run-time in the
+    // |SocketPermission| class.
+    { APIPermission::kSocket, "socket",
+      APIPermissionInfo::kFlagCannotBeOptional, 0,
+      PermissionMessage::kNone, &CreateAPIPermission<SocketPermission> },
+    { APIPermission::kAppCurrentWindowInternal, "app.currentWindowInternal" },
+    { APIPermission::kAppRuntime, "app.runtime" },
+    { APIPermission::kAppWindow, "app.window" },
+    { APIPermission::kAudioCapture, "audioCapture",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_AUDIO_CAPTURE,
+      PermissionMessage::kAudioCapture },
+    { APIPermission::kVideoCapture, "videoCapture",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_VIDEO_CAPTURE,
+      PermissionMessage::kVideoCapture },
+    // The permission string for "fileSystem" is only shown when "write" is
+    // present. Read-only access is only granted after the user has been shown
+    // a file chooser dialog and selected a file. Selecting the file is
+    // considered consent to read it.
+    { APIPermission::kFileSystem, "fileSystem" },
+    { APIPermission::kFileSystemWrite, "fileSystem.write",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_FILE_SYSTEM_WRITE,
+      PermissionMessage::kFileSystemWrite },
+    // Because warning messages for the "mediaGalleries" permission vary based
+    // on the permissions parameters, no message ID or message text is
+    // specified here.
+    // The message ID and text used will be determined at run-time in the
+    // |MediaGalleriesPermission| class.
+    { APIPermission::kMediaGalleries, "mediaGalleries",
+      APIPermissionInfo::kFlagNone, 0,
+      PermissionMessage::kNone,
+      &CreateAPIPermission<MediaGalleriesPermission> },
+    { APIPermission::kPushMessaging, "pushMessaging",
+      APIPermissionInfo::kFlagCannotBeOptional },
+    { APIPermission::kBluetooth, "bluetooth", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_BLUETOOTH,
+      PermissionMessage::kBluetooth },
+    { APIPermission::kBluetoothDevice, "bluetoothDevices",
+      APIPermissionInfo::kFlagNone, 0, PermissionMessage::kNone,
+      &CreateAPIPermission<BluetoothDevicePermission> },
+    { APIPermission::kUsb, "usb", APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_USB,
+      PermissionMessage::kUsb },
+    { APIPermission::kUsbDevice, "usbDevices",
+      APIPermissionInfo::kFlagMustBeOptional, 0, PermissionMessage::kNone,
+      &CreateAPIPermission<UsbDevicePermission> },
+    { APIPermission::kSystemIndicator, "systemIndicator",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_SYSTEM_INDICATOR,
+      PermissionMessage::kSystemIndicator },
+    { APIPermission::kSystemInfoDisplay, "systemInfo.display" },
+    { APIPermission::kPointerLock, "pointerLock" },
+    { APIPermission::kFullscreen, "fullscreen" },
+  };
+
+  std::vector<APIPermissionInfo*> permissions;
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(PermissionsToRegister); ++i) {
+    const PermissionRegistration& pr = PermissionsToRegister[i];
+    permissions.push_back(new APIPermissionInfo(
+        pr.id, pr.name, pr.l10n_message_id,
+        pr.message_id ? pr.message_id : PermissionMessage::kNone,
+        pr.flags,
+        pr.constructor));
+  }
+  return permissions;
+}
+
+std::vector<PermissionsInfo::AliasInfo> ChromeAPIPermissions::GetAllAliases()
+    const {
+  // Register aliases.
+  std::vector<PermissionsInfo::AliasInfo> aliases;
+  aliases.push_back(PermissionsInfo::AliasInfo(
+      "unlimitedStorage", kOldUnlimitedStoragePermission));
+  aliases.push_back(PermissionsInfo::AliasInfo(
+      "tabs", kWindowsPermission));
+  return aliases;
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.h b/chrome/common/extensions/permissions/chrome_api_permissions.h
new file mode 100644
index 0000000..ec5283c
--- /dev/null
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_
+#define CHROME_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_
+
+#include <vector>
+
+#include "chrome/common/extensions/permissions/permissions_info.h"
+
+namespace extensions {
+
+// Registers the permissions used in Chrome with the PermissionsInfo global.
+class ChromeAPIPermissions : public PermissionsInfo::Delegate {
+ public:
+  virtual std::vector<APIPermissionInfo*> GetAllPermissions() const OVERRIDE;
+  virtual std::vector<PermissionsInfo::AliasInfo> GetAllAliases() const
+      OVERRIDE;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_PERMISSIONS_CHROME_API_PERMISSIONS_H_
diff --git a/chrome/common/extensions/permissions/media_galleries_permission_unittest.cc b/chrome/common/extensions/permissions/media_galleries_permission_unittest.cc
index 365c042..27ba20c4 100644
--- a/chrome/common/extensions/permissions/media_galleries_permission_unittest.cc
+++ b/chrome/common/extensions/permissions/media_galleries_permission_unittest.cc
@@ -5,9 +5,9 @@
 // These tests make sure MediaGalleriesPermission values are parsed correctly.
 
 #include "base/values.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
 #include "chrome/common/extensions/permissions/media_galleries_permission_data.h"
-#include "chrome/common/extensions/permissions/permissions_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using content::SocketPermissionRequest;
@@ -17,10 +17,10 @@
 
 namespace {
 
-class MediaGalleriesPermissionTest : public testing::Test {
+class MediaGalleriesPermissionTest : public ExtensionTest {
 };
 
-TEST(MediaGalleriesPermissionTest, GoodValues) {
+TEST_F(MediaGalleriesPermissionTest, GoodValues) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kMediaGalleries);
 
@@ -50,7 +50,7 @@
   EXPECT_TRUE(permission->FromValue(value.get()));
 }
 
-TEST(MediaGalleriesPermissionTest, BadValues) {
+TEST_F(MediaGalleriesPermissionTest, BadValues) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kMediaGalleries);
 
@@ -62,7 +62,7 @@
   EXPECT_FALSE(permission->FromValue(value.get()));
 }
 
-TEST(MediaGalleriesPermissionTest, Equal) {
+TEST_F(MediaGalleriesPermissionTest, Equal) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kMediaGalleries);
 
@@ -90,7 +90,7 @@
   EXPECT_TRUE(permission1->Equal(permission2.get()));
 }
 
-TEST(MediaGalleriesPermissionTest, ToFromValue) {
+TEST_F(MediaGalleriesPermissionTest, ToFromValue) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kMediaGalleries);
 
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 896f342d..4b23daf 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -10,11 +10,9 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
-#include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/features/feature.h"
-#include "chrome/common/extensions/incognito_handler.h"
-#include "chrome/common/extensions/manifest_handler.h"
 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "chrome/common/extensions/permissions/permissions_info.h"
@@ -77,19 +75,16 @@
 
 }  // namespace
 
-class PermissionsTest : public testing::Test {
+class PermissionsTest : public ExtensionTest {
  protected:
   virtual void SetUp() OVERRIDE {
-    testing::Test::SetUp();
-    (new BackgroundManifestHandler)->Register();
+    ExtensionTest::SetUp();
     (new ContentScriptsHandler)->Register();
     (new PluginsHandler)->Register();
-    (new IncognitoHandler)->Register();
   }
 
   virtual void TearDown() OVERRIDE {
-    ManifestHandler::ClearRegistryForTesting();
-    testing::Test::TearDown();
+    ExtensionTest::TearDown();
   }
 };
 
diff --git a/chrome/common/extensions/permissions/permissions_info.cc b/chrome/common/extensions/permissions/permissions_info.cc
index a7be3e1..bea35d0 100644
--- a/chrome/common/extensions/permissions/permissions_info.cc
+++ b/chrome/common/extensions/permissions/permissions_info.cc
@@ -4,14 +4,50 @@
 
 #include "chrome/common/extensions/permissions/permissions_info.h"
 
+#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/string_util.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
 
 namespace extensions {
 
+static base::LazyInstance<PermissionsInfo> g_permissions_info =
+    LAZY_INSTANCE_INITIALIZER;
+// ScopedTestingPermissionsInfo can override the global instance for testing.
+static PermissionsInfo* g_permissions_info_override = NULL;
+
 // static
 PermissionsInfo* PermissionsInfo::GetInstance() {
-  return Singleton<PermissionsInfo>::get();
+  if (!g_permissions_info_override)
+    g_permissions_info_override = g_permissions_info.Pointer();
+  return g_permissions_info_override;
+}
+
+// static
+void PermissionsInfo::SetForTesting(PermissionsInfo* info) {
+  g_permissions_info_override = info;
+}
+
+PermissionsInfo::~PermissionsInfo() {
+  STLDeleteContainerPairSecondPointers(id_map_.begin(), id_map_.end());
+}
+
+void PermissionsInfo::InitializeWithDelegate(
+    const PermissionsInfo::Delegate& delegate) {
+  // TODO(yoz): Change into a DCHECK once StartupHelper browser tests
+  // are changed to unit tests.
+  if (initialized_)
+    return;
+  std::vector<APIPermissionInfo*> permissions = delegate.GetAllPermissions();
+  std::vector<AliasInfo> aliases = delegate.GetAllAliases();
+
+  for (size_t i = 0; i < permissions.size(); ++i)
+    RegisterPermission(permissions[i]);
+  for (size_t i = 0; i < aliases.size(); ++i)
+    RegisterAlias(aliases[i].name, aliases[i].alias);
+
+  initialized_ = true;
 }
 
 const APIPermissionInfo* PermissionsInfo::GetByID(
@@ -51,44 +87,28 @@
   return StartsWithASCII(i->first, name + '.', true);
 }
 
-PermissionsInfo::~PermissionsInfo() {
-  for (IDMap::iterator i = id_map_.begin(); i != id_map_.end(); ++i)
-    delete i->second;
-}
-
 PermissionsInfo::PermissionsInfo()
     : hosted_app_permission_count_(0),
-      permission_count_(0) {
-  APIPermissionInfo::RegisterAllPermissions(this);
+      permission_count_(0),
+      initialized_(false) {
 }
 
 void PermissionsInfo::RegisterAlias(
     const char* name,
     const char* alias) {
-  DCHECK(name_map_.find(name) != name_map_.end());
-  DCHECK(name_map_.find(alias) == name_map_.end());
+  DCHECK(ContainsKey(name_map_, name));
+  DCHECK(!ContainsKey(name_map_, alias));
   name_map_[alias] = name_map_[name];
 }
 
-const APIPermissionInfo* PermissionsInfo::RegisterPermission(
-    APIPermission::ID id,
-    const char* name,
-    int l10n_message_id,
-    PermissionMessage::ID message_id,
-    int flags,
-    const APIPermissionInfo::APIPermissionConstructor constructor) {
-  DCHECK(id_map_.find(id) == id_map_.end());
-  DCHECK(name_map_.find(name) == name_map_.end());
+void PermissionsInfo::RegisterPermission(APIPermissionInfo* permission) {
+  DCHECK(!ContainsKey(id_map_, permission->id()));
+  DCHECK(!ContainsKey(name_map_, permission->name()));
 
-  APIPermissionInfo* permission = new APIPermissionInfo(
-      id, name, l10n_message_id, message_id, flags, constructor);
-
-  id_map_[id] = permission;
-  name_map_[name] = permission;
+  id_map_[permission->id()] = permission;
+  name_map_[permission->name()] = permission;
 
   permission_count_++;
-
-  return permission;
 }
 
 }  // namespace extensions
diff --git a/chrome/common/extensions/permissions/permissions_info.h b/chrome/common/extensions/permissions/permissions_info.h
index e79abb4..a014511 100644
--- a/chrome/common/extensions/permissions/permissions_info.h
+++ b/chrome/common/extensions/permissions/permissions_info.h
@@ -10,20 +10,48 @@
 #include <string>
 
 #include "base/callback.h"
-#include "base/memory/singleton.h"
+#include "base/lazy_instance.h"
 #include "chrome/common/extensions/permissions/api_permission.h"
 #include "chrome/common/extensions/permissions/api_permission_set.h"
 #include "chrome/common/extensions/permissions/permission_message.h"
 
 namespace extensions {
 
-// Singleton that holds the extension permission instances and provides static
+class ChromeAPIPermissions;
+
+// A global object that holds the extension permission instances and provides
 // methods for accessing them.
 class PermissionsInfo {
  public:
-  // Returns a pointer to the singleton instance.
+  // An alias for a given permission |name|.
+  struct AliasInfo {
+    const char* name;
+    const char* alias;
+
+    AliasInfo(const char* name, const char* alias)
+        : name(name), alias(alias) {
+    }
+  };
+
+  // The delegate creates the APIPermissions instances. It is only
+  // needed at startup time.
+  class Delegate {
+   public:
+    // Returns all the known permissions. The caller, PermissionsInfo,
+    // takes ownership of the APIPermissionInfos.
+    virtual std::vector<APIPermissionInfo*> GetAllPermissions() const = 0;
+
+    // Returns all the known permission aliases.
+    virtual std::vector<AliasInfo> GetAllAliases() const = 0;
+  };
+
   static PermissionsInfo* GetInstance();
 
+  virtual ~PermissionsInfo();
+
+  // Initializes the permissions from the delegate.
+  void InitializeWithDelegate(const Delegate& delegate);
+
   // Returns the permission with the given |id|, and NULL if it doesn't exist.
   const APIPermissionInfo* GetByID(APIPermission::ID id) const;
 
@@ -46,22 +74,20 @@
   size_t get_permission_count() const { return permission_count_; }
 
  private:
-  friend class APIPermissionInfo;
+  friend class ScopedTestingPermissionsInfo;
+  friend struct base::DefaultLazyInstanceTraits<PermissionsInfo>;
 
-  ~PermissionsInfo();
   PermissionsInfo();
 
+  // Overrides the global PermissionsInfo for unit tests. Should only be
+  // called from ScopedTestingPermissionsInfo.
+  static void SetForTesting(PermissionsInfo* info);
+
   // Registers an |alias| for a given permission |name|.
   void RegisterAlias(const char* name, const char* alias);
 
   // Registers a permission with the specified attributes and flags.
-  const APIPermissionInfo* RegisterPermission(
-      APIPermission::ID id,
-      const char* name,
-      int l10n_message_id,
-      PermissionMessage::ID message_id,
-      int flags,
-      const APIPermissionInfo::APIPermissionConstructor constructor);
+  void RegisterPermission(APIPermissionInfo* permission);
 
   // Maps permission ids to permissions.
   typedef std::map<APIPermission::ID, APIPermissionInfo*> IDMap;
@@ -75,7 +101,9 @@
   size_t hosted_app_permission_count_;
   size_t permission_count_;
 
-  friend struct DefaultSingletonTraits<PermissionsInfo>;
+  // Set to true after the delegate has created the known permissions.
+  bool initialized_;
+
   DISALLOW_COPY_AND_ASSIGN(PermissionsInfo);
 };
 
diff --git a/chrome/common/extensions/permissions/scoped_testing_permissions_info.cc b/chrome/common/extensions/permissions/scoped_testing_permissions_info.cc
new file mode 100644
index 0000000..81919e0
--- /dev/null
+++ b/chrome/common/extensions/permissions/scoped_testing_permissions_info.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/permissions/scoped_testing_permissions_info.h"
+
+namespace extensions {
+
+ScopedTestingPermissionsInfo::ScopedTestingPermissionsInfo()
+    : info_(new PermissionsInfo),
+      old_info_(PermissionsInfo::GetInstance()) {
+  PermissionsInfo::SetForTesting(info_.get());
+}
+
+ScopedTestingPermissionsInfo::ScopedTestingPermissionsInfo(
+    const PermissionsInfo::Delegate& delegate)
+    : info_(new PermissionsInfo),
+      old_info_(PermissionsInfo::GetInstance()) {
+  PermissionsInfo::SetForTesting(info_.get());
+  PermissionsInfo::GetInstance()->InitializeWithDelegate(delegate);
+}
+
+ScopedTestingPermissionsInfo::~ScopedTestingPermissionsInfo() {
+  PermissionsInfo::SetForTesting(old_info_);
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/permissions/scoped_testing_permissions_info.h b/chrome/common/extensions/permissions/scoped_testing_permissions_info.h
new file mode 100644
index 0000000..a1d84cd
--- /dev/null
+++ b/chrome/common/extensions/permissions/scoped_testing_permissions_info.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_PERMISSIONS_SCOPED_TESTING_PERMISSIONS_INFO_H_
+#define CHROME_COMMON_EXTENSIONS_PERMISSIONS_SCOPED_TESTING_PERMISSIONS_INFO_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/common/extensions/permissions/permissions_info.h"
+
+namespace extensions {
+
+// Overrides the global PermissionsInfo on construction and restores it
+// on destruction.
+class ScopedTestingPermissionsInfo {
+ public:
+  ScopedTestingPermissionsInfo();
+  explicit ScopedTestingPermissionsInfo(
+      const PermissionsInfo::Delegate& delegate);
+  ~ScopedTestingPermissionsInfo();
+
+ private:
+  scoped_ptr<PermissionsInfo> info_;
+  PermissionsInfo* old_info_;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_PERMISSIONS_SCOPED_TESTING_PERMISSIONS_INFO_H_
diff --git a/chrome/common/extensions/permissions/socket_permission_unittest.cc b/chrome/common/extensions/permissions/socket_permission_unittest.cc
index 5232c87..6bb07be 100644
--- a/chrome/common/extensions/permissions/socket_permission_unittest.cc
+++ b/chrome/common/extensions/permissions/socket_permission_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/pickle.h"
 #include "base/values.h"
-#include "chrome/common/extensions/permissions/permissions_info.h"
+#include "chrome/common/extensions/extension_unittest.h"
 #include "chrome/common/extensions/permissions/socket_permission.h"
 #include "chrome/common/extensions/permissions/socket_permission_data.h"
 #include "ipc/ipc_message.h"
@@ -26,10 +26,10 @@
   EXPECT_EQ(expected_result, data.GetAsStringForTest());
 }
 
-class SocketPermissionTest : public testing::Test {
+class SocketPermissionTest : public ExtensionTest {
 };
 
-TEST(SocketPermissionTest, General) {
+TEST_F(SocketPermissionTest, General) {
   SocketPermissionData data1, data2;
 
   CHECK(data1.ParseForTest("tcp-connect"));
@@ -45,7 +45,7 @@
   EXPECT_TRUE(data1 < data2);
 }
 
-TEST(SocketPermissionTest, Parse) {
+TEST_F(SocketPermissionTest, Parse) {
   SocketPermissionData data;
 
   EXPECT_FALSE(data.ParseForTest(""));
@@ -119,7 +119,7 @@
   ParseTest("tcp-connect:*.example.com:99", "tcp-connect:*.example.com:99");
 }
 
-TEST(SocketPermissionTest, Match) {
+TEST_F(SocketPermissionTest, Match) {
   SocketPermissionData data;
   scoped_ptr<SocketPermission::CheckParam> param;
 
@@ -200,7 +200,7 @@
   EXPECT_FALSE(data.Check(param.get()));
 }
 
-TEST(SocketPermissionTest, IPC) {
+TEST_F(SocketPermissionTest, IPC) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);
 
@@ -242,7 +242,7 @@
   }
 }
 
-TEST(SocketPermissionTest, Value) {
+TEST_F(SocketPermissionTest, Value) {
   const APIPermissionInfo* permission_info =
     PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket);
 
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 1de8a7c..0cbd5d9 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -32,6 +32,7 @@
 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
 #include "chrome/common/extensions/manifest_handlers/sandboxed_page_info.h"
 #include "chrome/common/extensions/manifest_url_handler.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
 #include "chrome/common/extensions/web_accessible_resources_handler.h"
 #include "chrome/common/external_ipc_fuzzer.h"
 #include "chrome/common/localized_error.h"
@@ -338,6 +339,9 @@
   WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy(
       extension_resource_scheme);
 
+  extensions::ChromeAPIPermissions permissions;
+  extensions::PermissionsInfo::GetInstance()->InitializeWithDelegate(
+      permissions);
   RegisterExtensionManifestHandlers();
 
   RegisterRequestOSFileHandleAllowedHosts(
diff --git a/chrome/test/data/extensions/api_test/browser_action/synthesized/manifest.json b/chrome/test/data/extensions/manifest_tests/browser_action_synthesizes_command.json
similarity index 100%
rename from chrome/test/data/extensions/api_test/browser_action/synthesized/manifest.json
rename to chrome/test/data/extensions/manifest_tests/browser_action_synthesizes_command.json
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index d2606317..fcbd16a 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -28,6 +28,7 @@
 #include "chrome/common/extensions/extension_l10n_util.h"
 #include "chrome/common/extensions/incognito_handler.h"
 #include "chrome/common/extensions/manifest.h"
+#include "chrome/common/extensions/permissions/chrome_api_permissions.h"
 #include "chrome/common/extensions/unpacker.h"
 #include "chrome/common/extensions/update_manifest.h"
 #include "chrome/common/safe_browsing/zip_analyzer.h"
@@ -145,6 +146,9 @@
     int creation_flags) {
   CHECK(location > extensions::Manifest::INVALID_LOCATION);
   CHECK(location < extensions::Manifest::NUM_LOCATIONS);
+  extensions::ChromeAPIPermissions permissions;
+  extensions::PermissionsInfo::GetInstance()->InitializeWithDelegate(
+      permissions);
   RegisterExtensionManifestHandlers();
   extensions::Unpacker unpacker(
       extension_path,