Move Plugins out of Extension class

BUG=159265
[email protected]

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187043 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/content_settings/content_settings_internal_extension_provider.cc b/chrome/browser/content_settings/content_settings_internal_extension_provider.cc
index d6d7f78..815d037 100644
--- a/chrome/browser/content_settings/content_settings_internal_extension_provider.cc
+++ b/chrome/browser/content_settings/content_settings_internal_extension_provider.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -10,6 +10,7 @@
 #include "chrome/common/chrome_notification_types.h"
 #include "chrome/common/content_settings.h"
 #include "chrome/common/content_settings_pattern.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_set.h"
 #include "content/public/browser/browser_thread.h"
@@ -28,7 +29,7 @@
   const ExtensionSet* extensions = extension_service->extensions();
   for (ExtensionSet::const_iterator it = extensions->begin();
        it != extensions->end(); ++it) {
-    if ((*it)->plugins().size() > 0)
+    if (extensions::PluginInfo::HasPlugins(*it))
       SetContentSettingForExtension(*it, CONTENT_SETTING_ALLOW);
   }
   Profile* profile = extension_service->profile();
@@ -77,14 +78,14 @@
     case chrome::NOTIFICATION_EXTENSION_LOADED: {
       const extensions::Extension* extension =
           content::Details<extensions::Extension>(details).ptr();
-      if (extension->plugins().size() > 0)
+      if (extensions::PluginInfo::HasPlugins(extension))
         SetContentSettingForExtension(extension, CONTENT_SETTING_ALLOW);
       break;
     }
     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
       const UnloadedExtensionInfo& info =
           *(content::Details<UnloadedExtensionInfo>(details).ptr());
-      if (info.extension->plugins().size() > 0)
+      if (extensions::PluginInfo::HasPlugins(info.extension))
         SetContentSettingForExtension(info.extension, CONTENT_SETTING_DEFAULT);
       break;
     }
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api.cc b/chrome/browser/extensions/api/omnibox/omnibox_api.cc
index 81f0fa44b..e7621b6 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api.cc
@@ -149,8 +149,8 @@
 OmniboxAPI::~OmniboxAPI() {
 }
 
-base::LazyInstance<ProfileKeyedAPIFactory<OmniboxAPI> >
-g_factory = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<ProfileKeyedAPIFactory<OmniboxAPI> >
+    g_factory = LAZY_INSTANCE_INITIALIZER;
 
 // static
 ProfileKeyedAPIFactory<OmniboxAPI>* OmniboxAPI::GetFactoryInstance() {
diff --git a/chrome/browser/extensions/api/plugins/plugins_api.cc b/chrome/browser/extensions/api/plugins/plugins_api.cc
new file mode 100644
index 0000000..e87232aa
--- /dev/null
+++ b/chrome/browser/extensions/api/plugins/plugins_api.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/browser/extensions/api/plugins/plugins_api.h"
+
+#include "base/lazy_instance.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
+
+namespace extensions {
+
+static base::LazyInstance<ProfileKeyedAPIFactory<PluginsAPI> >
+    g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ProfileKeyedAPIFactory<PluginsAPI>* PluginsAPI::GetFactoryInstance() {
+  return &g_factory.Get();
+}
+
+PluginsAPI::PluginsAPI(Profile* profile) {
+  (new PluginsHandler)->Register();
+}
+
+PluginsAPI::~PluginsAPI() {
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/plugins/plugins_api.h b/chrome/browser/extensions/api/plugins/plugins_api.h
new file mode 100644
index 0000000..8ebc6e07
--- /dev/null
+++ b/chrome/browser/extensions/api/plugins/plugins_api.h
@@ -0,0 +1,36 @@
+// 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_BROWSER_EXTENSIONS_API_PLUGINS_PLUGINS_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_PLUGINS_PLUGINS_API_H_
+
+#include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
+#include "chrome/browser/profiles/profile_keyed_service.h"
+
+class Profile;
+
+namespace extensions {
+
+// The profile-keyed service that manages the plugins for extensions.
+class PluginsAPI : public ProfileKeyedAPI {
+ public:
+  explicit PluginsAPI(Profile* profile);
+  virtual ~PluginsAPI();
+
+  // ProfileKeyedAPI implementation.
+  static ProfileKeyedAPIFactory<PluginsAPI>* GetFactoryInstance();
+
+ private:
+  friend class ProfileKeyedAPIFactory<PluginsAPI>;
+
+  // ProfileKeyedAPI implementation.
+  static const char* service_name() { return "PluginsAPI"; }
+
+  DISALLOW_COPY_AND_ASSIGN(PluginsAPI);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_PLUGINS_PLUGINS_API_H_
+
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index b0bbe12..1767574 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -76,6 +76,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.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_file_util.h"
@@ -1101,19 +1102,25 @@
 #if defined(ENABLE_PLUGINS)
   // TODO(mpcomplete): This ends up affecting all profiles. See crbug.com/80757.
   bool plugins_changed = false;
-  for (size_t i = 0; i < extension->plugins().size(); ++i) {
-    const Extension::PluginInfo& plugin = extension->plugins()[i];
-    PluginService::GetInstance()->RefreshPlugins();
-    PluginService::GetInstance()->AddExtraPluginPath(plugin.path);
+  if (extensions::PluginInfo::HasPlugins(extension)) {
+    const extensions::PluginInfo::PluginVector* plugins =
+      extensions::PluginInfo::GetPlugins(extension);
+    CHECK(plugins);
     plugins_changed = true;
-    ChromePluginServiceFilter* filter =
-        ChromePluginServiceFilter::GetInstance();
-    if (plugin.is_public) {
-      filter->RestrictPluginToProfileAndOrigin(
-          plugin.path, profile_, GURL());
-    } else {
-      filter->RestrictPluginToProfileAndOrigin(
-          plugin.path, profile_, extension->url());
+    for (extensions::PluginInfo::PluginVector::const_iterator plugin =
+             plugins->begin();
+         plugin != plugins->end(); ++plugin) {
+      PluginService::GetInstance()->RefreshPlugins();
+      PluginService::GetInstance()->AddExtraPluginPath(plugin->path);
+      ChromePluginServiceFilter* filter =
+          ChromePluginServiceFilter::GetInstance();
+      if (plugin->is_public) {
+        filter->RestrictPluginToProfileAndOrigin(
+            plugin->path, profile_, GURL());
+      } else {
+        filter->RestrictPluginToProfileAndOrigin(
+            plugin->path, profile_, extension->url());
+      }
     }
   }
 
@@ -1184,13 +1191,18 @@
 
 #if defined(ENABLE_PLUGINS)
   bool plugins_changed = false;
-  for (size_t i = 0; i < extension->plugins().size(); ++i) {
-    const Extension::PluginInfo& plugin = extension->plugins()[i];
-    PluginService::GetInstance()->ForcePluginShutdown(plugin.path);
-    PluginService::GetInstance()->RefreshPlugins();
-    PluginService::GetInstance()->RemoveExtraPluginPath(plugin.path);
+  if (extensions::PluginInfo::HasPlugins(extension)) {
+    const extensions::PluginInfo::PluginVector* plugins =
+      extensions::PluginInfo::GetPlugins(extension);
     plugins_changed = true;
-    ChromePluginServiceFilter::GetInstance()->UnrestrictPlugin(plugin.path);
+    for (extensions::PluginInfo::PluginVector::const_iterator plugin =
+             plugins->begin();
+         plugin != plugins->end(); ++plugin) {
+      PluginService::GetInstance()->ForcePluginShutdown(plugin->path);
+      PluginService::GetInstance()->RefreshPlugins();
+      PluginService::GetInstance()->RemoveExtraPluginPath(plugin->path);
+      ChromePluginServiceFilter::GetInstance()->UnrestrictPlugin(plugin->path);
+    }
   }
 
   bool nacl_modules_changed = false;
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 225a0c54..420582a 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -63,6 +63,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/i18n/default_locale_handler.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_l10n_util.h"
@@ -547,6 +548,7 @@
   ExtensionErrorReporter::GetInstance()->ClearErrors();
   (new extensions::BackgroundManifestHandler)->Register();
   (new extensions::DefaultLocaleHandler)->Register();
+  (new extensions::PluginsHandler)->Register();
 }
 
 void ExtensionServiceTestBase::TearDown() {
@@ -1168,7 +1170,7 @@
   expected_path = extension->path().AppendASCII("script2.js");
   ASSERT_TRUE(file_util::AbsolutePath(&expected_path));
   EXPECT_TRUE(resource01.ComparePathWithDefault(expected_path));
-  EXPECT_TRUE(extension->plugins().empty());
+  EXPECT_TRUE(!extensions::PluginInfo::HasPlugins(extension));
   EXPECT_EQ(1u, scripts[1].url_patterns().patterns().size());
   EXPECT_EQ("http://*.news.com/*",
             scripts[1].url_patterns().begin()->GetAsString());
@@ -1192,17 +1194,22 @@
   EXPECT_EQ(loaded_[1]->GetResourceURL("background.html"),
             extensions::BackgroundInfo::GetBackgroundURL(loaded_[1]));
   EXPECT_EQ(0u, loaded_[1]->content_scripts().size());
+
   // We don't parse the plugins section on Chrome OS.
 #if defined(OS_CHROMEOS)
-  EXPECT_EQ(0u, loaded_[1]->plugins().size());
+  EXPECT_TRUE(!extensions::PluginInfo::HasPlugins(loaded_[1]));
 #else
-  ASSERT_EQ(2u, loaded_[1]->plugins().size());
+  ASSERT_TRUE(extensions::PluginInfo::HasPlugins(loaded_[1]));
+  const std::vector<extensions::PluginInfo>* plugins =
+      extensions::PluginInfo::GetPlugins(loaded_[1]);
+  ASSERT_TRUE(plugins);
+  ASSERT_EQ(2u, plugins->size());
   EXPECT_EQ(loaded_[1]->path().AppendASCII("content_plugin.dll").value(),
-            loaded_[1]->plugins()[0].path.value());
-  EXPECT_TRUE(loaded_[1]->plugins()[0].is_public);
+            plugins->at(0).path.value());
+  EXPECT_TRUE(plugins->at(0).is_public);
   EXPECT_EQ(loaded_[1]->path().AppendASCII("extension_plugin.dll").value(),
-            loaded_[1]->plugins()[1].path.value());
-  EXPECT_FALSE(loaded_[1]->plugins()[1].is_public);
+            plugins->at(1).path.value());
+  EXPECT_FALSE(plugins->at(1).is_public);
 #endif
 
   EXPECT_EQ(Manifest::INTERNAL, loaded_[1]->location());
diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc
index c840a28..a880a24 100644
--- a/chrome/browser/extensions/unpacked_installer.cc
+++ b/chrome/browser/extensions/unpacked_installer.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -15,6 +15,7 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/permissions_updater.h"
 #include "chrome/browser/extensions/requirements_checker.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_file_util.h"
 #include "chrome/common/extensions/manifest.h"
@@ -256,7 +257,7 @@
       service_weak_->disabled_extensions();
   if (service_weak_->show_extensions_prompts() &&
       prompt_for_plugins_ &&
-      !extension_->plugins().empty() &&
+      PluginInfo::HasPlugins(extension_) &&
       !disabled_extensions->Contains(extension_->id())) {
     SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
         service_weak_->profile(),
diff --git a/chrome/browser/profiles/profile_dependency_manager.cc b/chrome/browser/profiles/profile_dependency_manager.cc
index ddf47e16..daeb2fc 100644
--- a/chrome/browser/profiles/profile_dependency_manager.cc
+++ b/chrome/browser/profiles/profile_dependency_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -37,6 +37,7 @@
 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
 #include "chrome/browser/extensions/api/page_launcher/page_launcher_api.h"
+#include "chrome/browser/extensions/api/plugins/plugins_api.h"
 #include "chrome/browser/extensions/api/preference/preference_api.h"
 #include "chrome/browser/extensions/api/processes/processes_api.h"
 #include "chrome/browser/extensions/api/push_messaging/push_messaging_api.h"
@@ -299,6 +300,7 @@
 #endif
   extensions::OmniboxAPI::GetFactoryInstance();
   extensions::PageLauncherAPI::GetFactoryInstance();
+  extensions::PluginsAPI::GetFactoryInstance();
   extensions::PreferenceAPI::GetFactoryInstance();
   extensions::ProcessesAPI::GetFactoryInstance();
   extensions::PushMessagingAPI::GetFactoryInstance();
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 0244822..633eeca 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
 
@@ -283,6 +283,8 @@
         'browser/extensions/api/permissions/permissions_api.h',
         'browser/extensions/api/permissions/permissions_api_helpers.cc',
         'browser/extensions/api/permissions/permissions_api_helpers.h',
+        'browser/extensions/api/plugins/plugins_api.cc',
+        'browser/extensions/api/plugins/plugins_api.h',
         'browser/extensions/api/preference/preference_api.cc',
         'browser/extensions/api/preference/preference_api.h',
         'browser/extensions/api/preference/preference_api_constants.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index e684ce2..37aea2e 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
 
@@ -176,6 +176,8 @@
         'common/extensions/api/omnibox/omnibox_handler.h',
         'common/extensions/api/page_launcher/page_launcher_handler.cc',
         'common/extensions/api/page_launcher/page_launcher_handler.h',
+        'common/extensions/api/plugins/plugins_handler.cc',
+        'common/extensions/api/plugins/plugins_handler.h',
         'common/extensions/api/speech/tts_engine_manifest_handler.cc',
         'common/extensions/api/speech/tts_engine_manifest_handler.h',
         'common/extensions/api/themes/theme_handler.cc',
@@ -502,6 +504,8 @@
             ['include', 'common/extensions/api/i18n/default_locale_handler.h'],
             ['include', 'common/extensions/api/identity/oauth2_manifest_handler.cc'],
             ['include', 'common/extensions/api/identity/oauth2_manifest_handler.h'],
+            ['include', 'common/extensions/api/plugins/plugins_handler.cc'],
+            ['include', 'common/extensions/api/plugins/plugins_handler.h'],
             ['include', 'common/extensions/api/themes/theme_handler.cc'],
             ['include', 'common/extensions/api/themes/theme_handler.h'],
           ],
diff --git a/chrome/common/extensions/api/plugins/plugins_handler.cc b/chrome/common/extensions/api/plugins/plugins_handler.cc
new file mode 100644
index 0000000..275203bf
--- /dev/null
+++ b/chrome/common/extensions/api/plugins/plugins_handler.cc
@@ -0,0 +1,119 @@
+// 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/api/plugins/plugins_handler.h"
+
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/common/extensions/extension_manifest_constants.h"
+#include "chrome/common/extensions/manifest.h"
+#include "extensions/common/error_utils.h"
+
+#if defined(OS_WIN)
+#include "base/win/metro.h"
+#endif
+
+namespace keys = extension_manifest_keys;
+
+namespace extensions {
+
+namespace {
+
+struct PluginManifestData : Extension::ManifestData {
+  // Optional list of NPAPI plugins and associated properties for an extension.
+  PluginInfo::PluginVector plugins;
+};
+
+}  // namespace
+
+PluginInfo::PluginInfo(const base::FilePath& plugin_path, bool plugin_is_public)
+    : path(plugin_path), is_public(plugin_is_public) {
+}
+
+PluginInfo::~PluginInfo() {
+}
+
+// static
+const PluginInfo::PluginVector* PluginInfo::GetPlugins(
+    const Extension* extension) {
+  PluginManifestData* data = static_cast<PluginManifestData*>(
+      extension->GetManifestData(keys::kPlugins));
+  return data ? &data->plugins : NULL;
+}
+
+// static
+bool PluginInfo::HasPlugins(const Extension* extension) {
+  PluginManifestData* data = static_cast<PluginManifestData*>(
+      extension->GetManifestData(keys::kPlugins));
+  return data && !data->plugins.empty() ? true : false;
+}
+
+PluginsHandler::PluginsHandler() {
+}
+
+PluginsHandler::~PluginsHandler() {
+}
+
+const std::vector<std::string> PluginsHandler::Keys() const {
+  return SingleKey(keys::kPlugins);
+}
+
+bool PluginsHandler::Parse(Extension* extension, string16* error) {
+  const ListValue* list_value = NULL;
+  if (!extension->manifest()->GetList(keys::kPlugins, &list_value)) {
+    *error = ASCIIToUTF16(extension_manifest_errors::kInvalidPlugins);
+    return false;
+  }
+
+  scoped_ptr<PluginManifestData> plugins_data(new PluginManifestData);
+
+  for (size_t i = 0; i < list_value->GetSize(); ++i) {
+    const DictionaryValue* plugin_value = NULL;
+    if (!list_value->GetDictionary(i, &plugin_value)) {
+      *error = ASCIIToUTF16(extension_manifest_errors::kInvalidPlugins);
+      return false;
+    }
+    // Get plugins[i].path.
+    std::string path_str;
+    if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) {
+      *error = ErrorUtils::FormatErrorMessageUTF16(
+          extension_manifest_errors::kInvalidPluginsPath, base::IntToString(i));
+      return false;
+    }
+
+    // Get plugins[i].content (optional).
+    bool is_public = false;
+    if (plugin_value->HasKey(keys::kPluginsPublic)) {
+      if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) {
+        *error = ErrorUtils::FormatErrorMessageUTF16(
+            extension_manifest_errors::kInvalidPluginsPublic,
+            base::IntToString(i));
+        return false;
+      }
+    }
+
+    // We don't allow extensions to load NPAPI plugins on Chrome OS, or under
+    // Windows 8 Metro mode, but still parse the entries to display consistent
+    // error messages. If the extension actually requires the plugins then
+    // LoadRequirements will prevent it loading.
+#if defined(OS_CHROMEOS)
+    continue;
+#elif defined(OS_WIN)
+    if (base::win::IsMetroProcess()) {
+      continue;
+    }
+#endif  // defined(OS_WIN).
+    plugins_data->plugins.push_back(PluginInfo(
+        extension->path().Append(base::FilePath::FromUTF8Unsafe(path_str)),
+        is_public));
+  }
+
+  if (!plugins_data->plugins.empty())
+    extension->SetManifestData(keys::kPlugins, plugins_data.release());
+
+  return true;
+}
+
+}  // namespace extensions
diff --git a/chrome/common/extensions/api/plugins/plugins_handler.h b/chrome/common/extensions/api/plugins/plugins_handler.h
new file mode 100644
index 0000000..d9dd636
--- /dev/null
+++ b/chrome/common/extensions/api/plugins/plugins_handler.h
@@ -0,0 +1,49 @@
+// 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_API_PLUGINS_PLUGINS_HANDLER_H_
+#define CHROME_COMMON_EXTENSIONS_API_PLUGINS_PLUGINS_HANDLER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/manifest_handler.h"
+
+namespace extensions {
+
+// An NPAPI plugin included in the extension.
+struct PluginInfo {
+  typedef std::vector<PluginInfo> PluginVector;
+
+  PluginInfo(const base::FilePath& plugin_path, bool plugin_is_public);
+  ~PluginInfo();
+
+  base::FilePath path;  // Path to the plugin.
+  bool is_public;  // False if only this extension can load this plugin.
+
+  // Return the plugins for a given |extensions|, or NULL if none exist.
+  static const PluginVector* GetPlugins(const Extension* extension);
+
+  // Return true if the given |extension| has plugins, and false otherwise.
+  static bool HasPlugins(const Extension* extension);
+};
+
+// Parses the "plugins" manifest key.
+class PluginsHandler : public ManifestHandler {
+ public:
+  PluginsHandler();
+  virtual ~PluginsHandler();
+
+  virtual bool Parse(Extension* extension, string16* error) OVERRIDE;
+
+ private:
+  virtual const std::vector<std::string> Keys() const OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(PluginsHandler);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_COMMON_EXTENSIONS_API_PLUGINS_PLUGINS_HANDLER_H_
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index b1db79d..1d8a2a3a 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -24,11 +24,12 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
-// TODO(rdevlin.cronin): Remove this once PageAction, BrowserAction, and
-// SystemIndicator have been moved out of Extension.
+// TODO(rdevlin.cronin): Remove these once all references have been removed as
+// part of crbug.com/159265.
 #include "chrome/common/extensions/api/extension_action/action_info.h"
 #include "chrome/common/extensions/api/extension_action/page_action_handler.h"
 #include "chrome/common/extensions/api/icons/icons_handler.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/api/themes/theme_handler.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/csp_handler.h"
@@ -57,7 +58,6 @@
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_WIN)
-#include "base/win/metro.h"
 #include "grit/generated_resources.h"
 #endif
 
@@ -923,9 +923,8 @@
   //
   // TODO(akalin): Relax this restriction once we've put in UI to
   // approve synced extensions.
-  if (!plugins().empty()) {
+  if (PluginInfo::HasPlugins(this))
     return SYNC_TYPE_NONE;
-  }
 
   switch (GetType()) {
     case Manifest::TYPE_EXTENSION:
@@ -1718,7 +1717,6 @@
 bool Extension::LoadSharedFeatures(string16* error) {
   if (!LoadDescription(error) ||
       !ManifestHandler::ParseExtension(this, error) ||
-      !LoadPlugins(error) ||
       !LoadNaClModules(error) ||
       !LoadSandboxedPages(error) ||
       !LoadRequirements(error) ||
@@ -1763,59 +1761,6 @@
   return true;
 }
 
-bool Extension::LoadPlugins(string16* error) {
-  if (!manifest_->HasKey(keys::kPlugins))
-    return true;
-
-  const ListValue* list_value = NULL;
-  if (!manifest_->GetList(keys::kPlugins, &list_value)) {
-    *error = ASCIIToUTF16(errors::kInvalidPlugins);
-    return false;
-  }
-
-  for (size_t i = 0; i < list_value->GetSize(); ++i) {
-    const DictionaryValue* plugin_value = NULL;
-    if (!list_value->GetDictionary(i, &plugin_value)) {
-      *error = ASCIIToUTF16(errors::kInvalidPlugins);
-      return false;
-    }
-    // Get plugins[i].path.
-    std::string path_str;
-    if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) {
-      *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidPluginsPath, base::IntToString(i));
-      return false;
-    }
-
-    // Get plugins[i].content (optional).
-    bool is_public = false;
-    if (plugin_value->HasKey(keys::kPluginsPublic)) {
-      if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) {
-        *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidPluginsPublic, base::IntToString(i));
-        return false;
-      }
-    }
-
-    // We don't allow extensions to load NPAPI plugins on Chrome OS, or under
-    // Windows 8 Metro mode, but still parse the entries to display consistent
-    // error messages. If the extension actually requires the plugins then
-    // LoadRequirements will prevent it loading.
-#if defined(OS_CHROMEOS)
-    continue;
-#elif defined(OS_WIN)
-    if (base::win::IsMetroProcess()) {
-      continue;
-    }
-#endif  // defined(OS_WIN).
-    plugins_.push_back(PluginInfo());
-    plugins_.back().path = path().Append(
-        base::FilePath::FromUTF8Unsafe(path_str));
-    plugins_.back().is_public = is_public;
-  }
-  return true;
-}
-
 bool Extension::LoadNaClModules(string16* error) {
   if (!manifest_->HasKey(keys::kNaClModules))
     return true;
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 03e8804..3ecb571 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -116,12 +116,6 @@
     bool npapi;
   };
 
-  // An NPAPI plugin included in the extension.
-  struct PluginInfo {
-    base::FilePath path;  // Path to the plugin.
-    bool is_public;  // False if only this extension can load this plugin.
-  };
-
   // An NaCl module included in the extension.
   struct NaClModuleInfo {
     GURL url;
@@ -463,7 +457,6 @@
   const ActionInfo* system_indicator_info() const {
     return system_indicator_info_.get();
   }
-  const std::vector<PluginInfo>& plugins() const { return plugins_; }
   const std::vector<NaClModuleInfo>& nacl_modules() const {
     return nacl_modules_;
   }
@@ -604,10 +597,9 @@
   bool LoadSharedFeatures(string16* error);
   bool LoadDescription(string16* error);
   bool LoadManifestVersion(string16* error);
-  bool LoadPlugins(string16* error);
   bool LoadNaClModules(string16* error);
   bool LoadSandboxedPages(string16* error);
-  // Must be called after LoadPlugins().
+  // Must be called after the "plugins" key has been parsed.
   bool LoadRequirements(string16* error);
   bool LoadOfflineEnabled(string16* error);
   bool LoadExtensionFeatures(string16* error);
@@ -735,9 +727,6 @@
   // The extension's system indicator, if any.
   scoped_ptr<ActionInfo> system_indicator_info_;
 
-  // Optional list of NPAPI plugins and associated properties.
-  std::vector<PluginInfo> plugins_;
-
   // Optional list of NaCl modules and associated properties.
   std::vector<NaClModuleInfo> nacl_modules_;
 
diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc
index 7e9d6893..cd70e3f 100644
--- a/chrome/common/extensions/extension_file_util.cc
+++ b/chrome/common/extensions/extension_file_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -23,6 +23,7 @@
 #include "chrome/common/extensions/api/extension_action/browser_action_handler.h"
 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
 #include "chrome/common/extensions/api/icons/icons_handler.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/api/themes/theme_handler.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
@@ -331,14 +332,19 @@
   }
 
   // Validate claimed plugin paths.
-  for (size_t i = 0; i < extension->plugins().size(); ++i) {
-    const Extension::PluginInfo& plugin = extension->plugins()[i];
-    if (!file_util::PathExists(plugin.path)) {
-      *error =
-          l10n_util::GetStringFUTF8(
-              IDS_EXTENSION_LOAD_PLUGIN_PATH_FAILED,
-              plugin.path.LossyDisplayName());
+  if (extensions::PluginInfo::HasPlugins(extension)) {
+    const extensions::PluginInfo::PluginVector* plugins =
+        extensions::PluginInfo::GetPlugins(extension);
+    CHECK(plugins);
+    for (std::vector<extensions::PluginInfo>::const_iterator plugin =
+             plugins->begin();
+         plugin != plugins->end(); ++plugin) {
+      if (!file_util::PathExists(plugin->path)) {
+        *error = l10n_util::GetStringFUTF8(
+            IDS_EXTENSION_LOAD_PLUGIN_PATH_FAILED,
+            plugin->path.LossyDisplayName());
       return false;
+      }
     }
   }
 
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index e9a27b5..edc99bc 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -14,7 +14,7 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/extensions/api/commands/commands_handler.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_file_util.h"
@@ -103,8 +103,10 @@
 class ExtensionTest : public testing::Test {
  protected:
   virtual void SetUp() OVERRIDE {
+    testing::Test::SetUp();
     (new BackgroundManifestHandler)->Register();
     (new CommandsHandler)->Register();
+    (new PluginsHandler)->Register();
   }
 
   virtual void TearDown() OVERRIDE {
diff --git a/chrome/common/extensions/permissions/permission_set.cc b/chrome/common/extensions/permissions/permission_set.cc
index b235a4d..6e76426b 100644
--- a/chrome/common/extensions/permissions/permission_set.cc
+++ b/chrome/common/extensions/permissions/permission_set.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -8,6 +8,7 @@
 #include <iterator>
 #include <string>
 
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/manifest_url_handler.h"
 #include "chrome/common/extensions/permissions/permissions_info.h"
@@ -527,8 +528,9 @@
 void PermissionSet::InitImplicitExtensionPermissions(
     const extensions::Extension* extension) {
   // Add the implied permissions.
-  if (!extension->plugins().empty())
+  if (extensions::PluginInfo::HasPlugins(extension))
     apis_.insert(APIPermission::kPlugin);
+
   if (!ManifestURL::GetDevToolsPage(extension).is_empty())
     apis_.insert(APIPermission::kDevtools);
 
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index c035f91..cdb7bb1 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -9,10 +9,11 @@
 #include "base/utf_string_conversions.h"
 #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_manifest_constants.h"
 #include "chrome/common/extensions/features/feature.h"
+#include "chrome/common/extensions/manifest_handler.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "chrome/common/extensions/permissions/permissions_info.h"
 #include "chrome/common/extensions/permissions/socket_permission.h"
@@ -21,10 +22,6 @@
 
 using extensions::Extension;
 
-namespace errors = extension_manifest_errors;
-namespace keys = extension_manifest_keys;
-namespace values = extension_manifest_values;
-
 namespace extensions {
 
 namespace {
@@ -83,7 +80,14 @@
 
 class PermissionsTest : public testing::Test {
   virtual void SetUp() OVERRIDE {
+    testing::Test::SetUp();
     (new BackgroundManifestHandler)->Register();
+    (new PluginsHandler)->Register();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    ManifestHandler::ClearRegistryForTesting();
+    testing::Test::TearDown();
   }
 };
 
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index c135677f..0572ab9 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/extensions/api/extension_action/page_action_handler.h"
 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
 #include "chrome/common/extensions/api/icons/icons_handler.h"
+#include "chrome/common/extensions/api/plugins/plugins_handler.h"
 #include "chrome/common/extensions/api/themes/theme_handler.h"
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
@@ -60,6 +61,7 @@
   (new extensions::IconsHandler)->Register();
   (new extensions::PageActionHandler)->Register();
   (new extensions::ThemeHandler)->Register();
+  (new extensions::PluginsHandler)->Register();
 }
 
 }  // namespace