summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authordubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 14:40:02 +0000
committerdubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-18 14:40:02 +0000
commit25ae0154d995ac643a1e61d43107ba10e2ee55ec (patch)
tree99545fea59a9c2d6eece030917b884e7967d1dd9 /chrome/browser
parent3b0836d46ed49b74e52fdaa32d4b7b9545b10b2e (diff)
downloadchromium_src-25ae0154d995ac643a1e61d43107ba10e2ee55ec.zip
chromium_src-25ae0154d995ac643a1e61d43107ba10e2ee55ec.tar.gz
chromium_src-25ae0154d995ac643a1e61d43107ba10e2ee55ec.tar.bz2
Add policies to specify an enterprise web store.
Admins can specify the URL, name, and icon to be used for the enterprise web store. The app itself is implemented as a component extension, with some of the manifest values being specified by policy. BUG=88464 TEST=New ComponentLoaderTest class added to unit_tests. Additional manual testing: set "EnterpriseWebStoreURL" policy to a valid URL and start up Chrome. Verify that there is an app on the new tab page that links to the specified URL. If possible, try installing an extension from somewhere on that URL, and ensure that no warning is shown before showing the permission dialog. Review URL: http://codereview.chromium.org/8477005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110698 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/chromeos/accessibility_util.cc7
-rw-r--r--chrome/browser/extensions/component_loader.cc286
-rw-r--r--chrome/browser/extensions/component_loader.h70
-rw-r--r--chrome/browser/extensions/component_loader_unittest.cc252
-rw-r--r--chrome/browser/extensions/extension_service.cc9
-rw-r--r--chrome/browser/extensions/extension_service.h17
-rw-r--r--chrome/browser/extensions/settings/settings_frontend_unittest.cc8
-rw-r--r--chrome/browser/extensions/settings/settings_sync_unittest.cc2
-rw-r--r--chrome/browser/extensions/settings/settings_test_util.cc2
-rw-r--r--chrome/browser/extensions/settings/settings_test_util.h2
-rw-r--r--chrome/browser/extensions/test_extension_service.cc15
-rw-r--r--chrome/browser/extensions/test_extension_service.h8
-rw-r--r--chrome/browser/intents/web_intents_registry_unittest.cc36
-rw-r--r--chrome/browser/policy/configuration_policy_handler_list.cc4
-rw-r--r--chrome/browser/prefs/browser_prefs.cc2
-rw-r--r--chrome/browser/resources/enterprise_webstore_app/manifest.json17
17 files changed, 556 insertions, 182 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f1b86ad..a9e39bd 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -116,6 +116,7 @@
<include name="IDR_TRACING_JS" file="resources\tracing.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_TRANSLATE_JS" file="resources\translate.js" type="BINDATA" />
<include name="IDR_WEBSTORE_MANIFEST" file="resources\webstore_app\manifest.json" type="BINDATA" />
+ <include name="IDR_ENTERPRISE_WEBSTORE_MANIFEST" file="resources\enterprise_webstore_app\manifest.json" type="BINDATA" />
<if expr="not pp_ifdef('chromeos')">
<include name="IDR_SYNC_PROMO_JS" file="resources\sync_promo.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_SYNC_PROMO_HTML" file="resources\sync_promo.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
diff --git a/chrome/browser/chromeos/accessibility_util.cc b/chrome/browser/chromeos/accessibility_util.cc
index 7670909..7eb49e1 100644
--- a/chrome/browser/chromeos/accessibility_util.cc
+++ b/chrome/browser/chromeos/accessibility_util.cc
@@ -108,13 +108,12 @@ void EnableAccessibility(bool enabled, WebUI* login_web_ui) {
Profile* profile = ProfileManager::GetDefaultProfile();
ExtensionService* extension_service =
profile->GetExtensionService();
- std::string manifest = ResourceBundle::GetSharedInstance().
- GetRawDataResource(IDR_CHROMEVOX_MANIFEST).as_string();
FilePath path = FilePath(extension_misc::kAccessExtensionPath)
.AppendASCII(extension_misc::kChromeVoxDirectoryName);
if (enabled) { // Load ChromeVox
const Extension* extension =
- extension_service->component_loader()->Add(manifest, path);
+ extension_service->component_loader()->Add(IDR_CHROMEVOX_MANIFEST,
+ path);
if (login_web_ui) {
RenderViewHost* render_view_host =
@@ -136,7 +135,7 @@ void EnableAccessibility(bool enabled, WebUI* login_web_ui) {
LOG(INFO) << "ChromeVox was Loaded.";
} else { // Unload ChromeVox
- extension_service->component_loader()->Remove(manifest);
+ extension_service->component_loader()->Remove(path);
LOG(INFO) << "ChromeVox was Unloaded.";
}
}
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 624befa..87b48d7 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -9,10 +9,16 @@
#include "base/json/json_value_serializer.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/prefs/pref_change_registrar.h"
+#include "chrome/browser/prefs/pref_notifier.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/pref_names.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
#include "grit/browser_resources.h"
#include "ui/base/resource/resource_bundle.h"
@@ -20,41 +26,19 @@
#include "chrome/browser/defaults.h"
#endif
-namespace {
-
-typedef std::list<std::pair<FilePath::StringType, int> >
- ComponentExtensionList;
-
-#if defined(FILE_MANAGER_EXTENSION)
-void AddFileManagerExtension(ComponentExtensionList* component_extensions) {
-#ifndef NDEBUG
- const CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) {
- FilePath filemgr_extension_path =
- command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath);
- component_extensions->push_back(std::make_pair(
- filemgr_extension_path.value(),
- IDR_FILEMANAGER_MANIFEST));
- return;
- }
-#endif // NDEBUG
- component_extensions->push_back(std::make_pair(
- FILE_PATH_LITERAL("file_manager"),
- IDR_FILEMANAGER_MANIFEST));
-}
-#endif // defined(FILE_MANAGER_EXTENSION)
-
-} // namespace
-
namespace extensions {
-bool ComponentLoader::ComponentExtensionInfo::Equals(
- const ComponentExtensionInfo& other) const {
- return other.manifest == manifest && other.root_directory == root_directory;
-}
+ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service,
+ PrefService* prefs,
+ PrefService* local_state)
+ : prefs_(prefs),
+ local_state_(local_state),
+ extension_service_(extension_service) {
+ pref_change_registrar_.Init(prefs);
-ComponentLoader::ComponentLoader(ExtensionService* extension_service)
- : extension_service_(extension_service) {
+ // This pref is set by policy. We have to watch it for change because on
+ // ChromeOS, policy isn't loaded until after the browser process is started.
+ pref_change_registrar_.Add(prefs::kEnterpriseWebStoreURL, this);
}
ComponentLoader::~ComponentLoader() {
@@ -68,23 +52,60 @@ void ComponentLoader::LoadAll() {
}
}
+DictionaryValue* ComponentLoader::ParseManifest(
+ const std::string& manifest_contents) const {
+ JSONStringValueSerializer serializer(manifest_contents);
+ scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
+
+ if (!manifest.get() || !manifest->IsType(Value::TYPE_DICTIONARY)) {
+ LOG(ERROR) << "Failed to parse extension manifest.";
+ return NULL;
+ }
+ // Transfer ownership to the caller.
+ return static_cast<DictionaryValue*>(manifest.release());
+}
+
+const Extension* ComponentLoader::Add(
+ int manifest_resource_id,
+ const FilePath& root_directory) {
+ std::string manifest_contents =
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ manifest_resource_id).as_string();
+ return Add(manifest_contents, root_directory);
+}
+
+const Extension* ComponentLoader::Add(
+ std::string& manifest_contents,
+ const FilePath& root_directory) {
+ // The Value is kept for the lifetime of the ComponentLoader. This is
+ // required in case LoadAll() is called again.
+ DictionaryValue* manifest = ParseManifest(manifest_contents);
+ if (manifest)
+ return Add(manifest, root_directory);
+ return NULL;
+}
+
const Extension* ComponentLoader::Add(
- const std::string& manifest, const FilePath& root_directory) {
- ComponentExtensionInfo info(manifest, root_directory);
- Register(info);
+ const DictionaryValue* parsed_manifest,
+ const FilePath& root_directory) {
+ // Get the absolute path to the extension.
+ FilePath absolute_path(root_directory);
+ if (!absolute_path.IsAbsolute()) {
+ if (PathService::Get(chrome::DIR_RESOURCES, &absolute_path)) {
+ absolute_path = absolute_path.Append(root_directory);
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ ComponentExtensionInfo info(parsed_manifest, absolute_path);
+ component_extensions_.push_back(info);
if (extension_service_->is_ready())
return Load(info);
return NULL;
}
const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) {
- JSONStringValueSerializer serializer(info.manifest);
- scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
- if (!manifest.get()) {
- LOG(ERROR) << "Failed to parse manifest for extension";
- return NULL;
- }
-
int flags = Extension::REQUIRE_KEY;
if (Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT))
flags |= Extension::STRICT_ERROR_CHECKS;
@@ -92,7 +113,7 @@ const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) {
scoped_refptr<const Extension> extension(Extension::Create(
info.root_directory,
Extension::COMPONENT,
- *static_cast<DictionaryValue*>(manifest.get()),
+ *info.manifest,
flags,
&error));
if (!extension.get()) {
@@ -103,19 +124,29 @@ const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) {
return extension;
}
-void ComponentLoader::Remove(const std::string& manifest_str) {
- // Unload the extension.
- JSONStringValueSerializer serializer(manifest_str);
- scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
- if (!manifest.get()) {
- LOG(ERROR) << "Failed to parse manifest for extension";
- return;
+void ComponentLoader::Remove(const FilePath& root_directory) {
+ // Find the ComponentExtensionInfo for the extension.
+ RegisteredComponentExtensions::iterator it = component_extensions_.begin();
+ for (; it != component_extensions_.end(); ++it) {
+ if (it->root_directory == root_directory)
+ break;
}
+ // If the extension is not in the list, there's nothing to do.
+ if (it == component_extensions_.end())
+ return;
+
+ const DictionaryValue* manifest = it->manifest;
+
+ // Remove the extension from the list of registered extensions.
+ *it = component_extensions_.back();
+ component_extensions_.pop_back();
+
+ // Determine the extension id and unload the extension.
std::string public_key;
std::string public_key_bytes;
std::string id;
- if (!static_cast<DictionaryValue*>(manifest.get())->
- GetString(extension_manifest_keys::kPublicKey, &public_key) ||
+ if (!manifest->GetString(
+ extension_manifest_keys::kPublicKey, &public_key) ||
!Extension::ParsePEMKeyBytes(public_key, &public_key_bytes) ||
!Extension::GenerateId(public_key_bytes, &id)) {
LOG(ERROR) << "Failed to get extension id";
@@ -123,106 +154,129 @@ void ComponentLoader::Remove(const std::string& manifest_str) {
}
extension_service_->
UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE);
+}
- // Unregister the extension.
- RegisteredComponentExtensions new_component_extensions;
- for (RegisteredComponentExtensions::iterator it =
- component_extensions_.begin();
- it != component_extensions_.end(); ++it) {
- if (it->manifest != manifest_str)
- new_component_extensions.push_back(*it);
+void ComponentLoader::AddFileManagerExtension() {
+#if defined(FILE_MANAGER_EXTENSION)
+#ifndef NDEBUG
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) {
+ FilePath filemgr_extension_path(
+ command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath));
+ Add(IDR_FILEMANAGER_MANIFEST, filemgr_extension_path);
+ return;
}
- component_extensions_.swap(new_component_extensions);
+#endif // NDEBUG
+ Add(IDR_FILEMANAGER_MANIFEST, FilePath(FILE_PATH_LITERAL("file_manager")));
+#endif // defined(FILE_MANAGER_EXTENSION)
}
-// We take ComponentExtensionList:
-// path, manifest ID => full manifest, absolute path
-void ComponentLoader::AddDefaultComponentExtensions() {
- ComponentExtensionList component_extensions;
+void ComponentLoader::AddOrReloadEnterpriseWebStore() {
+ FilePath path(FILE_PATH_LITERAL("enterprise_web_store"));
- // Bookmark manager.
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("bookmark_manager"),
- IDR_BOOKMARKS_MANIFEST));
+ // Remove the extension if it was already loaded.
+ Remove(path);
+
+ std::string enterprise_webstore_url =
+ prefs_->GetString(prefs::kEnterpriseWebStoreURL);
+
+ // Load the extension only if the URL preference is set.
+ if (!enterprise_webstore_url.empty()) {
+ std::string manifest_contents =
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_ENTERPRISE_WEBSTORE_MANIFEST).as_string();
+
+ // The manifest is missing some values that are provided by policy.
+ DictionaryValue* manifest = ParseManifest(manifest_contents);
+ if (manifest) {
+ std::string name = prefs_->GetString(prefs::kEnterpriseWebStoreName);
+ manifest->SetString("app.launch.web_url", enterprise_webstore_url);
+ manifest->SetString("name", name);
+ Add(manifest, path);
+ }
+ }
+}
+
+void ComponentLoader::AddDefaultComponentExtensions() {
+ Add(IDR_BOOKMARKS_MANIFEST, FilePath(FILE_PATH_LITERAL("bookmark_manager")));
#if defined(FILE_MANAGER_EXTENSION)
- AddFileManagerExtension(&component_extensions);
+ AddFileManagerExtension();
#endif
#if defined(USE_VIRTUAL_KEYBOARD)
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("keyboard"),
- IDR_KEYBOARD_MANIFEST));
+ Add(IDR_KEYBOARD_MANIFEST, FilePath(FILE_PATH_LITERAL("keyboard")));
#endif
#if defined(OS_CHROMEOS)
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile"),
- IDR_MOBILE_MANIFEST));
+ Add(IDR_MOBILE_MANIFEST,
+ FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile")));
const CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kAuthExtensionPath)) {
FilePath auth_extension_path =
command_line->GetSwitchValuePath(switches::kAuthExtensionPath);
- component_extensions.push_back(std::make_pair(
- auth_extension_path.value(),
- IDR_GAIA_TEST_AUTH_MANIFEST));
+ Add(IDR_GAIA_TEST_AUTH_MANIFEST, auth_extension_path);
} else {
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth"),
- IDR_GAIA_AUTH_MANIFEST));
+ Add(IDR_GAIA_AUTH_MANIFEST,
+ FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/gaia_auth")));
}
#if defined(OFFICIAL_BUILD)
if (browser_defaults::enable_help_app) {
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp"),
- IDR_HELP_MANIFEST));
+ Add(IDR_HELP_MANIFEST,
+ FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp")));
}
#endif
-#endif
+#endif // !defined(OS_CHROMEOS)
- // Web Store.
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("web_store"),
- IDR_WEBSTORE_MANIFEST));
+ Add(IDR_WEBSTORE_MANIFEST, FilePath(FILE_PATH_LITERAL("web_store")));
#if !defined(OS_CHROMEOS)
// Cloud Print component app. Not required on Chrome OS.
- component_extensions.push_back(std::make_pair(
- FILE_PATH_LITERAL("cloud_print"),
- IDR_CLOUDPRINT_MANIFEST));
-#endif // !defined(OS_CHROMEOS)
-
- for (ComponentExtensionList::iterator iter = component_extensions.begin();
- iter != component_extensions.end(); ++iter) {
- FilePath path(iter->first);
- if (!path.IsAbsolute()) {
- if (PathService::Get(chrome::DIR_RESOURCES, &path)) {
- path = path.Append(iter->first);
- } else {
- NOTREACHED();
- }
- }
-
- std::string manifest =
- ResourceBundle::GetSharedInstance().GetRawDataResource(
- iter->second).as_string();
- Add(manifest, path);
- }
+ Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print")));
+#endif
#if defined(OS_CHROMEOS)
// Register access extensions only if accessibility is enabled.
- if (g_browser_process->local_state()->
- GetBoolean(prefs::kAccessibilityEnabled)) {
+ if (local_state_->GetBoolean(prefs::kAccessibilityEnabled)) {
FilePath path = FilePath(extension_misc::kAccessExtensionPath)
.AppendASCII(extension_misc::kChromeVoxDirectoryName);
- std::string manifest =
- ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_CHROMEVOX_MANIFEST).as_string();
- Add(manifest, path);
+ Add(IDR_CHROMEVOX_MANIFEST, path);
}
#endif
+
+ // If a URL for the enterprise webstore has been specified, load the
+ // component extension. This extension might also be loaded later, because
+ // it is specified by policy, and on ChromeOS policies are loaded after
+ // the browser process has started.
+ AddOrReloadEnterpriseWebStore();
+}
+
+void ComponentLoader::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == chrome::NOTIFICATION_PREF_CHANGED) {
+ const std::string* name =
+ content::Details<const std::string>(details).ptr();
+ if (*name == prefs::kEnterpriseWebStoreURL)
+ AddOrReloadEnterpriseWebStore();
+ else
+ NOTREACHED();
+ } else {
+ NOTREACHED();
+ }
+}
+
+// static
+void ComponentLoader::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterStringPref(prefs::kEnterpriseWebStoreURL,
+ std::string() /* default_value */,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterStringPref(prefs::kEnterpriseWebStoreName,
+ std::string() /* default_value */,
+ PrefService::UNSYNCABLE_PREF);
}
} // namespace extensions
diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h
index 6961098..8dca8a1 100644
--- a/chrome/browser/extensions/component_loader.h
+++ b/chrome/browser/extensions/component_loader.h
@@ -9,30 +9,41 @@
#include <string>
#include "base/file_path.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_change_registrar.h"
+#include "content/public/browser/notification_observer.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
class Extension;
-class ExtensionService;
+class ExtensionServiceInterface;
+class PrefService;
namespace extensions {
// For registering, loading, and unloading component extensions.
-class ComponentLoader {
+class ComponentLoader : public content::NotificationObserver {
public:
- explicit ComponentLoader(ExtensionService* extension_service);
+ ComponentLoader(ExtensionServiceInterface* extension_service,
+ PrefService* prefs,
+ PrefService* local_state);
virtual ~ComponentLoader();
// Loads any registered component extensions.
void LoadAll();
- // Loads and registers a component extension. If ExtensionService has been
- // initialized, the extension is loaded; otherwise, the load is deferred
- // until LoadAll is called.
- const Extension* Add(const std::string& manifest,
+ // Registers and possibly loads a component extension. If ExtensionService
+ // has been initialized, the extension is loaded; otherwise, the load is
+ // deferred until LoadAll is called.
+ const Extension* Add(std::string& manifest_contents,
+ const FilePath& root_directory);
+
+ // Convenience method for registering a component extension by resource id.
+ const Extension* Add(int manifest_resource_id,
const FilePath& root_directory);
// Unloads a component extension and removes it from the list of component
// extensions to be loaded.
- void Remove(const std::string& manifest_str);
+ void Remove(const FilePath& root_directory);
// Adds the default component extensions.
//
@@ -44,38 +55,59 @@ class ComponentLoader {
// openssl rsa -pubout -outform DER < /tmp/key.pem 2>/dev/null | base64 -w 0
void AddDefaultComponentExtensions();
+ // content::NotificationObserver implementation
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // Parse the given JSON manifest. Returns NULL if it cannot be parsed, or if
+ // if the result is not a DictionaryValue.
+ DictionaryValue* ParseManifest(const std::string& manifest_contents) const;
+
+ // Clear the list of registered extensions.
+ void ClearAllRegistered() {
+ component_extensions_.clear();
+ }
+
private:
// Information about a registered component extension.
struct ComponentExtensionInfo {
- ComponentExtensionInfo(const std::string& manifest,
+ ComponentExtensionInfo(const DictionaryValue* manifest,
const FilePath& root_directory)
: manifest(manifest),
root_directory(root_directory) {
}
- bool Equals(const ComponentExtensionInfo& other) const;
-
- // The extension's manifest. This is required for component extensions so
- // that ExtensionService doesn't need to go to disk to load them.
- std::string manifest;
+ // The parsed contents of the extensions's manifest file.
+ const DictionaryValue* manifest;
// Directory where the extension is stored.
FilePath root_directory;
};
+ const Extension* Add(const DictionaryValue* parsed_manifest,
+ const FilePath& root_directory);
+
// Loads a registered component extension.
const Extension* Load(const ComponentExtensionInfo& info);
- // Registers an extension to be loaded as a component extension.
- void Register(const ComponentExtensionInfo& info) {
- component_extensions_.push_back(info);
- }
+ void AddFileManagerExtension();
+
+ // Add the enterprise webstore extension, or reload it if already loaded.
+ void AddOrReloadEnterpriseWebStore();
+
+ PrefService* prefs_;
+ PrefService* local_state_;
+
+ ExtensionServiceInterface* extension_service_;
// List of registered component extensions (see Extension::Location).
typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions;
RegisteredComponentExtensions component_extensions_;
- ExtensionService* extension_service_;
+ PrefChangeRegistrar pref_change_registrar_;
};
} // namespace extensions
diff --git a/chrome/browser/extensions/component_loader_unittest.cc b/chrome/browser/extensions/component_loader_unittest.cc
new file mode 100644
index 0000000..3e3d8c8
--- /dev/null
+++ b/chrome/browser/extensions/component_loader_unittest.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2011 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 <string>
+
+#include "chrome/browser/extensions/component_loader.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/extensions/test_extension_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockExtensionService : public TestExtensionService {
+ private:
+ bool ready_;
+ ExtensionList extension_list_;
+
+ public:
+ MockExtensionService() : ready_(false) {
+ }
+
+ virtual void AddExtension(const Extension* extension) OVERRIDE {
+ // ExtensionService must become the owner of the extension object.
+ extension_list_.push_back(extension);
+ }
+
+ virtual void UnloadExtension(
+ const std::string& extension_id,
+ extension_misc::UnloadedExtensionReason reason) OVERRIDE {
+ // Remove the extension with the matching id.
+ for (ExtensionList::iterator it = extension_list_.begin();
+ it != extension_list_.end();
+ ++it) {
+ if ((*it)->id() == extension_id) {
+ extension_list_.erase(it);
+ return;
+ }
+ }
+ }
+
+ virtual bool is_ready() OVERRIDE {
+ return ready_;
+ }
+
+ virtual const ExtensionList* extensions() const OVERRIDE {
+ return &extension_list_;
+ }
+
+ void set_ready(bool ready) {
+ ready_ = ready;
+ }
+
+ void clear_extension_list() {
+ extension_list_.clear();
+ }
+};
+
+} // namespace
+
+namespace extensions {
+
+class ComponentLoaderTest : public testing::Test {
+ public:
+ ComponentLoaderTest() :
+ // Note: we pass the same pref service here, to stand in for both
+ // user prefs and local state.
+ component_loader_(&extension_service_, &prefs_, &prefs_) {
+ }
+
+ void SetUp() {
+ FilePath test_data_dir;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
+ extension_path_ =
+ test_data_dir.AppendASCII("extensions")
+ .AppendASCII("good")
+ .AppendASCII("Extensions")
+ .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
+ .AppendASCII("1.0.0.0");
+
+ // Read in the extension manifest.
+ ASSERT_TRUE(file_util::ReadFileToString(
+ extension_path_.Append(Extension::kManifestFilename),
+ &manifest_contents_));
+
+ // Register the user prefs that ComponentLoader will read.
+ prefs_.RegisterStringPref(prefs::kEnterpriseWebStoreURL, std::string());
+ prefs_.RegisterStringPref(prefs::kEnterpriseWebStoreName, std::string());
+
+ // Register the local state prefs.
+#if defined(OS_CHROMEOS)
+ prefs_.RegisterBooleanPref(prefs::kAccessibilityEnabled, false);
+#endif
+ }
+
+ protected:
+ MockExtensionService extension_service_;
+ TestingPrefService prefs_;
+ ComponentLoader component_loader_;
+
+ // The root directory of the text extension.
+ FilePath extension_path_;
+
+ // The contents of the text extension's manifest file.
+ std::string manifest_contents_;
+};
+
+TEST_F(ComponentLoaderTest, ParseManifest) {
+ scoped_ptr<DictionaryValue> manifest;
+
+ // Test invalid JSON.
+ manifest.reset(
+ component_loader_.ParseManifest("{ 'test': 3 } invalid"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ // Test manifests that are valid JSON, but don't have an object literal
+ // at the root. ParseManifest() should always return NULL.
+
+ manifest.reset(component_loader_.ParseManifest(""));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("[{ \"foo\": 3 }]"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("\"Test\""));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("42"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("true"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("false"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ manifest.reset(component_loader_.ParseManifest("null"));
+ ASSERT_EQ((DictionaryValue*)NULL, manifest.get());
+
+ // Test parsing valid JSON.
+
+ int value;
+ manifest.reset(component_loader_.ParseManifest(
+ "{ \"test\": { \"one\": 1 }, \"two\": 2 }"));
+ ASSERT_NE(manifest.get(), (DictionaryValue*)NULL);
+ ASSERT_TRUE(manifest->GetInteger("test.one", &value));
+ ASSERT_EQ(1, value);
+ ASSERT_TRUE(manifest->GetInteger("two", &value));
+ ASSERT_EQ(2, value);
+
+ std::string string_value;
+ manifest.reset(component_loader_.ParseManifest(manifest_contents_));
+ ASSERT_TRUE(manifest->GetString("background_page", &string_value));
+ ASSERT_EQ("backgroundpage.html", string_value);
+}
+
+// Test that the extension isn't loaded if the extension service isn't ready.
+TEST_F(ComponentLoaderTest, AddWhenNotReady) {
+ scoped_refptr<const Extension> extension;
+ extension_service_.set_ready(false);
+ extension = component_loader_.Add(manifest_contents_, extension_path_);
+ ASSERT_EQ((Extension*)NULL, extension.get());
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+}
+
+// Test that it *is* loaded when the extension service *is* ready.
+TEST_F(ComponentLoaderTest, AddWhenReady) {
+ scoped_refptr<const Extension> extension;
+ extension_service_.set_ready(true);
+ extension = component_loader_.Add(manifest_contents_, extension_path_);
+ ASSERT_NE((Extension*)NULL, extension.get());
+ ASSERT_EQ(1U, extension_service_.extensions()->size());
+}
+
+TEST_F(ComponentLoaderTest, Remove) {
+ extension_service_.set_ready(false);
+
+ // Removing an extension that was never added should be ok.
+ component_loader_.Remove(extension_path_);
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+
+ // Try adding and removing before LoadAll() is called.
+ component_loader_.Add(manifest_contents_, extension_path_);
+ component_loader_.Remove(extension_path_);
+ component_loader_.LoadAll();
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+
+ // Load an extension, and check that it's unloaded when Remove() is called.
+ scoped_refptr<const Extension> extension;
+ extension_service_.set_ready(true);
+ extension = component_loader_.Add(manifest_contents_, extension_path_);
+ ASSERT_NE((Extension*)NULL, extension.get());
+ component_loader_.Remove(extension_path_);
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+
+ // And after calling LoadAll(), it shouldn't get loaded.
+ component_loader_.LoadAll();
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+}
+
+TEST_F(ComponentLoaderTest, LoadAll) {
+ extension_service_.set_ready(false);
+
+ // No extensions should be loaded if none were added.
+ component_loader_.LoadAll();
+ ASSERT_EQ(0U, extension_service_.extensions()->size());
+
+ // Use LoadAll() to load the default extensions.
+ component_loader_.AddDefaultComponentExtensions();
+ component_loader_.LoadAll();
+ unsigned int default_count = extension_service_.extensions()->size();
+
+ // Clear the list of loaded extensions, and reload with one more.
+ extension_service_.clear_extension_list();
+ component_loader_.Add(manifest_contents_, extension_path_);
+ component_loader_.LoadAll();
+
+ ASSERT_EQ(default_count + 1, extension_service_.extensions()->size());
+}
+
+TEST_F(ComponentLoaderTest, EnterpriseWebStore) {
+ component_loader_.AddDefaultComponentExtensions();
+ component_loader_.LoadAll();
+ unsigned int default_count = extension_service_.extensions()->size();
+
+ // Set the pref, and it should get loaded automatically.
+ extension_service_.set_ready(true);
+ prefs_.SetUserPref(prefs::kEnterpriseWebStoreURL,
+ Value::CreateStringValue("http://www.google.com"));
+ ASSERT_EQ(default_count + 1, extension_service_.extensions()->size());
+
+ // Now that the pref is set, check if it's added by default.
+ extension_service_.set_ready(false);
+ extension_service_.clear_extension_list();
+ component_loader_.ClearAllRegistered();
+ component_loader_.AddDefaultComponentExtensions();
+ component_loader_.LoadAll();
+ ASSERT_EQ(default_count + 1, extension_service_.extensions()->size());
+
+ // Number of loaded extensions should be the same after changing the pref.
+ prefs_.SetUserPref(prefs::kEnterpriseWebStoreURL,
+ Value::CreateStringValue("http://www.google.de"));
+ ASSERT_EQ(default_count + 1, extension_service_.extensions()->size());
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 9f55624..f2f72ce 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -410,7 +410,10 @@ ExtensionService::ExtensionService(Profile* profile,
update_frequency));
}
- component_loader_.reset(new extensions::ComponentLoader(this));
+ component_loader_.reset(
+ new extensions::ComponentLoader(this,
+ profile->GetPrefs(),
+ g_browser_process->local_state()));
app_notification_manager_->Init();
@@ -1136,6 +1139,10 @@ ExtensionContentSettingsStore*
return extension_prefs()->content_settings_store();
}
+bool ExtensionService::is_ready() {
+ return ready_;
+}
+
ExtensionUpdater* ExtensionService::updater() {
return updater_.get();
}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 19ddee2..c072192 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -114,6 +114,14 @@ class ExtensionServiceInterface : public SyncableService {
// TODO(akalin): Remove this method (and others) once we refactor
// themes sync to not use it directly.
virtual void CheckForUpdatesSoon() = 0;
+
+ virtual void AddExtension(const Extension* extension) = 0;
+
+ virtual void UnloadExtension(
+ const std::string& extension_id,
+ extension_misc::UnloadedExtensionReason reason) = 0;
+
+ virtual bool is_ready() = 0;
};
// Manages installed and running Chromium extensions.
@@ -312,8 +320,9 @@ class ExtensionService
void CheckForExternalUpdates();
// Unload the specified extension.
- void UnloadExtension(const std::string& extension_id,
- extension_misc::UnloadedExtensionReason reason);
+ virtual void UnloadExtension(
+ const std::string& extension_id,
+ extension_misc::UnloadedExtensionReason reason);
// Unload all extensions. This is currently only called on shutdown, and
// does not send notifications.
@@ -360,7 +369,7 @@ class ExtensionService
// Adds |extension| to this ExtensionService and notifies observers than an
// extension has been loaded. Called by the backend after an extension has
// been loaded from a file and installed.
- void AddExtension(const Extension* extension);
+ virtual void AddExtension(const Extension* extension);
// Called by the backend when an extension has been installed.
void OnExtensionInstalled(
@@ -415,7 +424,7 @@ class ExtensionService
ExtensionContentSettingsStore* GetExtensionContentSettingsStore();
// Whether the extension service is ready.
- bool is_ready() { return ready_; }
+ virtual bool is_ready() OVERRIDE;
extensions::ComponentLoader* component_loader() {
return component_loader_.get();
diff --git a/chrome/browser/extensions/settings/settings_frontend_unittest.cc b/chrome/browser/extensions/settings/settings_frontend_unittest.cc
index e4a1433..7b2345f 100644
--- a/chrome/browser/extensions/settings/settings_frontend_unittest.cc
+++ b/chrome/browser/extensions/settings/settings_frontend_unittest.cc
@@ -80,7 +80,7 @@ class ExtensionSettingsFrontendTest : public testing::Test {
TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) {
const std::string id = "ext";
- profile_->GetMockExtensionService()->AddExtension(
+ profile_->GetMockExtensionService()->AddExtensionWithId(
id, Extension::TYPE_EXTENSION);
SettingsStorage* storage = GetStorage(id, frontend_.get());
@@ -111,7 +111,7 @@ TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) {
TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) {
const std::string id = "ext";
- profile_->GetMockExtensionService()->AddExtension(
+ profile_->GetMockExtensionService()->AddExtensionWithId(
id, Extension::TYPE_PACKAGED_APP);
SettingsStorage* storage = GetStorage(id, frontend_.get());
@@ -137,7 +137,7 @@ TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) {
TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) {
const std::string id = "ext";
- profile_->GetMockExtensionService()->AddExtension(
+ profile_->GetMockExtensionService()->AddExtensionWithId(
id, Extension::TYPE_EXTENSION);
SettingsStorage* storage = GetStorage(id, frontend_.get());
@@ -169,7 +169,7 @@ TEST_F(ExtensionSettingsFrontendTest,
LeveldbCreationFailureFailsAllOperations) {
const StringValue bar("bar");
const std::string id = "ext";
- profile_->GetMockExtensionService()->AddExtension(
+ profile_->GetMockExtensionService()->AddExtensionWithId(
id, Extension::TYPE_EXTENSION);
storage_factory_->Reset(new NullSettingsStorageFactory());
diff --git a/chrome/browser/extensions/settings/settings_sync_unittest.cc b/chrome/browser/extensions/settings/settings_sync_unittest.cc
index 2f22f57..70552da 100644
--- a/chrome/browser/extensions/settings/settings_sync_unittest.cc
+++ b/chrome/browser/extensions/settings/settings_sync_unittest.cc
@@ -194,7 +194,7 @@ class ExtensionSettingsSyncTest : public testing::Test {
// its storage area.
SettingsStorage* AddExtensionAndGetStorage(
const std::string& id, Extension::Type type) {
- profile_->GetMockExtensionService()->AddExtension(id, type);
+ profile_->GetMockExtensionService()->AddExtensionWithId(id, type);
return GetStorage(id, frontend_.get());
}
diff --git a/chrome/browser/extensions/settings/settings_test_util.cc b/chrome/browser/extensions/settings/settings_test_util.cc
index cc7fca1..c834c87 100644
--- a/chrome/browser/extensions/settings/settings_test_util.cc
+++ b/chrome/browser/extensions/settings/settings_test_util.cc
@@ -41,7 +41,7 @@ const Extension* MockExtensionService::GetExtensionById(
NULL : maybe_extension->second.get();
}
-void MockExtensionService::AddExtension(
+void MockExtensionService::AddExtensionWithId(
const std::string& id, Extension::Type type) {
DictionaryValue manifest;
manifest.SetString("name", std::string("Test extension ") + id);
diff --git a/chrome/browser/extensions/settings/settings_test_util.h b/chrome/browser/extensions/settings/settings_test_util.h
index f7cac9d..ce78a6e5 100644
--- a/chrome/browser/extensions/settings/settings_test_util.h
+++ b/chrome/browser/extensions/settings/settings_test_util.h
@@ -38,7 +38,7 @@ class MockExtensionService : public TestExtensionService {
virtual ~MockExtensionService();
// Adds an extension with id |id| to be returned by GetExtensionById.
- void AddExtension(const std::string& id, Extension::Type type);
+ void AddExtensionWithId(const std::string& id, Extension::Type type);
virtual const Extension* GetExtensionById(
const std::string& id, bool include_disabled) const OVERRIDE;
diff --git a/chrome/browser/extensions/test_extension_service.cc b/chrome/browser/extensions/test_extension_service.cc
index 277e8ea..9e0be6e 100644
--- a/chrome/browser/extensions/test_extension_service.cc
+++ b/chrome/browser/extensions/test_extension_service.cc
@@ -89,3 +89,18 @@ SyncError TestExtensionService::ProcessSyncChanges(
ADD_FAILURE();
return SyncError();
}
+
+bool TestExtensionService::is_ready() {
+ ADD_FAILURE();
+ return false;
+}
+
+void TestExtensionService::AddExtension(const Extension* extension) {
+ ADD_FAILURE();
+}
+
+void TestExtensionService::UnloadExtension(
+ const std::string& extension_id,
+ extension_misc::UnloadedExtensionReason reason) {
+ ADD_FAILURE();
+}
diff --git a/chrome/browser/extensions/test_extension_service.h b/chrome/browser/extensions/test_extension_service.h
index 6abf325..2e7d2e4 100644
--- a/chrome/browser/extensions/test_extension_service.h
+++ b/chrome/browser/extensions/test_extension_service.h
@@ -52,6 +52,14 @@ class TestExtensionService : public ExtensionServiceInterface {
virtual SyncError ProcessSyncChanges(
const tracked_objects::Location& from_here,
const SyncChangeList& change_list) OVERRIDE;
+
+ virtual bool is_ready() OVERRIDE;
+
+ virtual void AddExtension(const Extension* extension) OVERRIDE;
+
+ virtual void UnloadExtension(
+ const std::string& extension_id,
+ extension_misc::UnloadedExtensionReason reason) OVERRIDE;
};
#endif // CHROME_BROWSER_EXTENSIONS_TEST_EXTENSION_SERVICE_H_
diff --git a/chrome/browser/intents/web_intents_registry_unittest.cc b/chrome/browser/intents/web_intents_registry_unittest.cc
index 01a4cae..f6b20db 100644
--- a/chrome/browser/intents/web_intents_registry_unittest.cc
+++ b/chrome/browser/intents/web_intents_registry_unittest.cc
@@ -7,6 +7,7 @@
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/extensions/test_extension_service.h"
#include "chrome/browser/intents/web_intents_registry.h"
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/chrome_paths.h"
@@ -17,37 +18,10 @@
using content::BrowserThread;
using webkit_glue::WebIntentServiceData;
-class MockExtensionService: public ExtensionServiceInterface {
+class MockExtensionService: public TestExtensionService {
public:
virtual ~MockExtensionService() {}
MOCK_CONST_METHOD0(extensions, const ExtensionList*());
- MOCK_METHOD0(pending_extension_manager, PendingExtensionManager*());
- MOCK_METHOD4(UpdateExtension, bool(const std::string& id,
- const FilePath& path,
- const GURL& download_url,
- CrxInstaller** out_crx_installer));
- MOCK_CONST_METHOD2(GetExtensionById,
- const Extension*(const std::string& id,
- bool include_disabled));
- MOCK_CONST_METHOD1(GetInstalledExtension,
- const Extension*(const std::string& id));
- MOCK_CONST_METHOD1(IsExtensionEnabled,bool(const std::string& extension_id));
- MOCK_CONST_METHOD1(IsExternalExtensionUninstalled,
- bool(const std::string& extension_id));
- MOCK_METHOD1(UpdateExtensionBlacklist,
- void(const std::vector<std::string>& blacklist));
- MOCK_METHOD0(CheckAdminBlacklist, void());;
- MOCK_METHOD0(CheckForUpdatesSoon,void());
-
- MOCK_METHOD3(MergeDataAndStartSyncing,
- SyncError(syncable::ModelType type,
- const SyncDataList& initial_sync_data,
- SyncChangeProcessor* sync_processor));
- MOCK_METHOD1(StopSyncing, void(syncable::ModelType type));
- MOCK_CONST_METHOD1(GetAllSyncData, SyncDataList(syncable::ModelType type));
- MOCK_METHOD2(ProcessSyncChanges,
- SyncError(const tracked_objects::Location& from_here,
- const SyncChangeList& change_list));
};
// TODO(groby): Unify loading functions with extension_manifest_unittest code.
@@ -121,8 +95,8 @@ class WebIntentsRegistryTest : public testing::Test {
wds_ = new WebDataService();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
wds_->Init(temp_dir_.path());
- registry_.Initialize(wds_, &extensionService_);
- EXPECT_CALL(extensionService_, extensions()).
+ registry_.Initialize(wds_, &extension_service_);
+ EXPECT_CALL(extension_service_, extensions()).
WillRepeatedly(testing::Return(&extensions_));
}
@@ -139,7 +113,7 @@ class WebIntentsRegistryTest : public testing::Test {
content::TestBrowserThread ui_thread_;
content::TestBrowserThread db_thread_;
scoped_refptr<WebDataService> wds_;
- MockExtensionService extensionService_;
+ MockExtensionService extension_service_;
ExtensionList extensions_;
WebIntentsRegistry registry_;
ScopedTempDir temp_dir_;
diff --git a/chrome/browser/policy/configuration_policy_handler_list.cc b/chrome/browser/policy/configuration_policy_handler_list.cc
index 5e12167..edb616b 100644
--- a/chrome/browser/policy/configuration_policy_handler_list.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list.cc
@@ -188,6 +188,10 @@ const PolicyToPreferenceMapEntry kSimplePolicyMap[] = {
prefs::kUrlBlacklist },
{ Value::TYPE_LIST, kPolicyURLWhitelist,
prefs::kUrlWhitelist },
+ { Value::TYPE_STRING, kPolicyEnterpriseWebStoreURL,
+ prefs::kEnterpriseWebStoreURL },
+ { Value::TYPE_STRING, kPolicyEnterpriseWebStoreName,
+ prefs::kEnterpriseWebStoreName },
#if defined(OS_CHROMEOS)
{ Value::TYPE_BOOLEAN, kPolicyChromeOsLockOnIdleSuspend,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 0022c7d..63568a7 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -16,6 +16,7 @@
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/extensions/apps_promo.h"
+#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_web_ui.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
@@ -194,6 +195,7 @@ void RegisterUserPrefs(PrefService* user_prefs) {
#if !defined(OS_CHROMEOS)
default_apps::RegisterUserPrefs(user_prefs);
#endif
+ extensions::ComponentLoader::RegisterUserPrefs(user_prefs);
}
void MigrateBrowserPrefs(PrefService* user_prefs, PrefService* local_state) {
diff --git a/chrome/browser/resources/enterprise_webstore_app/manifest.json b/chrome/browser/resources/enterprise_webstore_app/manifest.json
new file mode 100644
index 0000000..462d82f
--- /dev/null
+++ b/chrome/browser/resources/enterprise_webstore_app/manifest.json
@@ -0,0 +1,17 @@
+{
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT/VTRfIVU2P0jPQOHFevNpKTFyKHReHOLaeYmCKK+T/GoXYJocfOOwyibhTqvwYequQPrK32Wjw4mbzkfTq5/LYYl2oXkoUL72FmJ5lMQcJsrJVGD0DfvjNpCPBEaCRkLnHPnJPw3NQlKb98aMMdmbfgiGvymBXekesKF3dg4dwIDAQAB",
+ "name": "", // Must be specified by policy.
+ "version": "0.1",
+ "description": "Enterprise Web Store",
+ "icons": {
+ },
+ "app": {
+ "launch": {
+ "web_url": "" // Must be specified by policy.
+ }
+ },
+ "permissions": [
+ "webstorePrivate",
+ "management"
+ ]
+}