component_loader.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/component_loader.h"
6
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/json/json_string_value_serializer.h"
10#include "base/path_service.h"
11#include "base/prefs/pref_notifier.h"
12#include "base/prefs/public/pref_change_registrar.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/prefs/pref_service.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/common/chrome_notification_types.h"
18#include "chrome/common/chrome_paths.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/extensions/extension.h"
21#include "chrome/common/extensions/extension_file_util.h"
22#include "chrome/common/extensions/extension_manifest_constants.h"
23#include "chrome/common/extensions/feature_switch.h"
24#include "chrome/common/pref_names.h"
25#include "content/public/browser/notification_details.h"
26#include "content/public/browser/notification_source.h"
27#include "grit/browser_resources.h"
28#include "ui/base/resource/resource_bundle.h"
29
30#if defined(OFFICIAL_BUILD)
31#include "chrome/browser/defaults.h"
32#endif
33
34#if defined(OS_CHROMEOS)
35#include "chrome/browser/chromeos/login/user_manager.h"
36#endif
37
38#if defined(USE_ASH)
39#include "grit/chromium_strings.h"
40#include "ui/base/l10n/l10n_util.h"
41#endif
42
43namespace extensions {
44
45namespace {
46
47std::string GenerateId(const DictionaryValue* manifest, const FilePath& path) {
48  std::string raw_key;
49  std::string id_input;
50  std::string id;
51  CHECK(manifest->GetString(extension_manifest_keys::kPublicKey, &raw_key));
52  CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input));
53  CHECK(Extension::GenerateId(id_input, &id));
54  return id;
55}
56
57}  // namespace
58
59ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
60    const DictionaryValue* manifest, const FilePath& directory)
61    : manifest(manifest),
62      root_directory(directory) {
63  if (!root_directory.IsAbsolute()) {
64    CHECK(PathService::Get(chrome::DIR_RESOURCES, &root_directory));
65    root_directory = root_directory.Append(directory);
66  }
67  extension_id = GenerateId(manifest, root_directory);
68}
69
70ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service,
71                                 PrefService* prefs,
72                                 PrefService* local_state)
73    : prefs_(prefs),
74      local_state_(local_state),
75      extension_service_(extension_service) {
76  pref_change_registrar_.Init(prefs);
77
78  // This pref is set by policy. We have to watch it for change because on
79  // ChromeOS, policy isn't loaded until after the browser process is started.
80  pref_change_registrar_.Add(prefs::kEnterpriseWebStoreURL, this);
81}
82
83ComponentLoader::~ComponentLoader() {
84  ClearAllRegistered();
85}
86
87const Extension* ComponentLoader::GetScriptBubble() const {
88  if (script_bubble_id_.empty())
89    return NULL;
90
91  return extension_service_->extensions()->GetByID(script_bubble_id_);
92}
93
94void ComponentLoader::LoadAll() {
95  for (RegisteredComponentExtensions::iterator it =
96          component_extensions_.begin();
97      it != component_extensions_.end(); ++it) {
98    Load(*it);
99  }
100}
101
102DictionaryValue* ComponentLoader::ParseManifest(
103    const std::string& manifest_contents) const {
104  JSONStringValueSerializer serializer(manifest_contents);
105  scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
106
107  if (!manifest.get() || !manifest->IsType(Value::TYPE_DICTIONARY)) {
108    LOG(ERROR) << "Failed to parse extension manifest.";
109    return NULL;
110  }
111  // Transfer ownership to the caller.
112  return static_cast<DictionaryValue*>(manifest.release());
113}
114
115void ComponentLoader::ClearAllRegistered() {
116  for (RegisteredComponentExtensions::iterator it =
117          component_extensions_.begin();
118      it != component_extensions_.end(); ++it) {
119      delete it->manifest;
120  }
121
122  component_extensions_.clear();
123}
124
125std::string ComponentLoader::Add(int manifest_resource_id,
126                                 const FilePath& root_directory) {
127  std::string manifest_contents =
128      ResourceBundle::GetSharedInstance().GetRawDataResource(
129          manifest_resource_id).as_string();
130  return Add(manifest_contents, root_directory);
131}
132
133std::string ComponentLoader::Add(const std::string& manifest_contents,
134                                 const FilePath& root_directory) {
135  // The Value is kept for the lifetime of the ComponentLoader. This is
136  // required in case LoadAll() is called again.
137  DictionaryValue* manifest = ParseManifest(manifest_contents);
138  if (manifest)
139    return Add(manifest, root_directory);
140  return "";
141}
142
143std::string ComponentLoader::Add(const DictionaryValue* parsed_manifest,
144                                 const FilePath& root_directory) {
145  ComponentExtensionInfo info(parsed_manifest, root_directory);
146  component_extensions_.push_back(info);
147  if (extension_service_->is_ready())
148    Load(info);
149  return info.extension_id;
150}
151
152std::string ComponentLoader::AddOrReplace(const FilePath& path) {
153  FilePath absolute_path = path;
154  file_util::AbsolutePath(&absolute_path);
155  std::string error;
156  scoped_ptr<DictionaryValue> manifest(
157      extension_file_util::LoadManifest(absolute_path, &error));
158  if (!manifest.get()) {
159    LOG(ERROR) << "Could not load extension from '" <<
160                  absolute_path.value() << "'. " << error;
161    return NULL;
162  }
163  Remove(GenerateId(manifest.get(), absolute_path));
164
165  return Add(manifest.release(), absolute_path);
166}
167
168void ComponentLoader::Reload(const std::string& extension_id) {
169  for (RegisteredComponentExtensions::iterator it =
170         component_extensions_.begin(); it != component_extensions_.end();
171         ++it) {
172    if (it->extension_id == extension_id) {
173      Load(*it);
174      break;
175    }
176  }
177}
178
179const Extension* ComponentLoader::Load(const ComponentExtensionInfo& info) {
180  // TODO(abarth): We should REQUIRE_MODERN_MANIFEST_VERSION once we've updated
181  //               our component extensions to the new manifest version.
182  int flags = Extension::REQUIRE_KEY;
183
184  std::string error;
185
186  scoped_refptr<const Extension> extension(Extension::Create(
187      info.root_directory,
188      Extension::COMPONENT,
189      *info.manifest,
190      flags,
191      &error));
192  if (!extension.get()) {
193    LOG(ERROR) << error;
194    return NULL;
195  }
196  CHECK_EQ(info.extension_id, extension->id()) << extension->name();
197  extension_service_->AddExtension(extension);
198  return extension;
199}
200
201void ComponentLoader::Remove(const FilePath& root_directory) {
202  // Find the ComponentExtensionInfo for the extension.
203  RegisteredComponentExtensions::iterator it = component_extensions_.begin();
204  for (; it != component_extensions_.end(); ++it) {
205    if (it->root_directory == root_directory) {
206      Remove(GenerateId(it->manifest, root_directory));
207      break;
208    }
209  }
210}
211
212void ComponentLoader::Remove(const std::string& id) {
213  RegisteredComponentExtensions::iterator it = component_extensions_.begin();
214  for (; it != component_extensions_.end(); ++it) {
215    if (it->extension_id == id) {
216      delete it->manifest;
217      it = component_extensions_.erase(it);
218      if (extension_service_->is_ready())
219        extension_service_->
220            UnloadExtension(id, extension_misc::UNLOAD_REASON_DISABLE);
221      break;
222    }
223  }
224}
225
226bool ComponentLoader::Exists(const std::string& id) const {
227  RegisteredComponentExtensions::const_iterator it =
228      component_extensions_.begin();
229  for (; it != component_extensions_.end(); ++it)
230    if (it->extension_id == id)
231      return true;
232  return false;
233}
234
235void ComponentLoader::AddFileManagerExtension() {
236#if defined(FILE_MANAGER_EXTENSION)
237  const CommandLine* command_line = CommandLine::ForCurrentProcess();
238  int manifest_id = command_line->HasSwitch(switches::kFileManagerPackaged) ?
239      IDR_FILEMANAGER_MANIFEST :
240      IDR_FILEMANAGER_MANIFEST_V1;
241#ifndef NDEBUG
242  if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) {
243    FilePath filemgr_extension_path(
244        command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath));
245    Add(manifest_id, filemgr_extension_path);
246    return;
247  }
248#endif  // NDEBUG
249  Add(manifest_id, FilePath(FILE_PATH_LITERAL("file_manager")));
250#endif  // defined(FILE_MANAGER_EXTENSION)
251}
252
253#if defined(OS_CHROMEOS)
254void ComponentLoader::AddGaiaAuthExtension() {
255  const CommandLine* command_line = CommandLine::ForCurrentProcess();
256  if (command_line->HasSwitch(switches::kAuthExtensionPath)) {
257    FilePath auth_extension_path =
258        command_line->GetSwitchValuePath(switches::kAuthExtensionPath);
259    Add(IDR_GAIA_TEST_AUTH_MANIFEST, auth_extension_path);
260    return;
261  }
262  Add(IDR_GAIA_AUTH_MANIFEST, FilePath(FILE_PATH_LITERAL("gaia_auth")));
263}
264#endif  // NDEBUG
265
266void ComponentLoader::AddOrReloadEnterpriseWebStore() {
267  FilePath path(FILE_PATH_LITERAL("enterprise_web_store"));
268
269  // Remove the extension if it was already loaded.
270  Remove(path);
271
272  std::string enterprise_webstore_url =
273      prefs_->GetString(prefs::kEnterpriseWebStoreURL);
274
275  // Load the extension only if the URL preference is set.
276  if (!enterprise_webstore_url.empty()) {
277    std::string manifest_contents =
278      ResourceBundle::GetSharedInstance().GetRawDataResource(
279          IDR_ENTERPRISE_WEBSTORE_MANIFEST).as_string();
280
281    // The manifest is missing some values that are provided by policy.
282    DictionaryValue* manifest = ParseManifest(manifest_contents);
283    if (manifest) {
284      std::string name = prefs_->GetString(prefs::kEnterpriseWebStoreName);
285      manifest->SetString("app.launch.web_url", enterprise_webstore_url);
286      manifest->SetString("name", name);
287      Add(manifest, path);
288    }
289  }
290}
291
292void ComponentLoader::AddChromeApp() {
293#if defined(USE_ASH)
294  std::string manifest_contents =
295      ResourceBundle::GetSharedInstance().GetRawDataResource(
296          IDR_CHROME_APP_MANIFEST).as_string();
297
298  // The Value is kept for the lifetime of the ComponentLoader. This is
299  // required in case LoadAll() is called again.
300  DictionaryValue* manifest = ParseManifest(manifest_contents);
301
302  // Update manifest to use a proper name.
303  manifest->SetString(extension_manifest_keys::kName,
304                      l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME));
305
306  if (manifest)
307    Add(manifest, FilePath(FILE_PATH_LITERAL("chrome_app")));
308#endif
309}
310
311void ComponentLoader::AddScriptBubble() {
312  if (FeatureSwitch::script_bubble()->IsEnabled()) {
313    script_bubble_id_ =
314        Add(IDR_SCRIPT_BUBBLE_MANIFEST,
315            FilePath(FILE_PATH_LITERAL("script_bubble")));
316  }
317}
318
319void ComponentLoader::AddDefaultComponentExtensions() {
320#if defined(OS_CHROMEOS)
321  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kGuestSession))
322    Add(IDR_BOOKMARKS_MANIFEST,
323        FilePath(FILE_PATH_LITERAL("bookmark_manager")));
324#else
325  Add(IDR_BOOKMARKS_MANIFEST, FilePath(FILE_PATH_LITERAL("bookmark_manager")));
326#endif
327
328#if defined(OS_CHROMEOS)
329  Add(IDR_WALLPAPERMANAGER_MANIFEST,
330      FilePath(FILE_PATH_LITERAL("chromeos/wallpaper_manager")));
331#endif
332
333#if defined(FILE_MANAGER_EXTENSION)
334  AddFileManagerExtension();
335#endif
336
337#if defined(OS_CHROMEOS)
338  const CommandLine* command_line = CommandLine::ForCurrentProcess();
339  if (command_line->HasSwitch(switches::kEnableBackgroundLoader)) {
340    Add(IDR_BACKLOADER_MANIFEST,
341        FilePath(FILE_PATH_LITERAL("backloader")));
342  }
343
344  Add(IDR_MOBILE_MANIFEST,
345      FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/mobile")));
346
347  Add(IDR_CROSH_BUILTIN_MANIFEST, FilePath(FILE_PATH_LITERAL(
348      "/usr/share/chromeos-assets/crosh_builtin")));
349
350  AddGaiaAuthExtension();
351
352  // TODO(gauravsh): Only include echo extension on official builds.
353  FilePath echo_extension_path(FILE_PATH_LITERAL(
354      "/usr/share/chromeos-assets/echo"));
355  if (command_line->HasSwitch(switches::kEchoExtensionPath)) {
356    echo_extension_path =
357        command_line->GetSwitchValuePath(switches::kEchoExtensionPath);
358  }
359  Add(IDR_ECHO_MANIFEST, echo_extension_path);
360
361#if defined(OFFICIAL_BUILD)
362  if (browser_defaults::enable_help_app) {
363    Add(IDR_HELP_MANIFEST,
364        FilePath(FILE_PATH_LITERAL("/usr/share/chromeos-assets/helpapp")));
365  }
366#endif
367#endif  // !defined(OS_CHROMEOS)
368
369  Add(IDR_WEBSTORE_MANIFEST, FilePath(FILE_PATH_LITERAL("web_store")));
370
371#if defined(OS_WIN)
372  if (CommandLine::ForCurrentProcess()->HasSwitch(
373          switches::kEnableSettingsApp)) {
374    Add(IDR_SETTINGS_APP_MANIFEST,
375        FilePath(FILE_PATH_LITERAL("settings_app")));
376  }
377#endif
378
379#if !defined(OS_CHROMEOS)
380  // Cloud Print component app. Not required on Chrome OS.
381  Add(IDR_CLOUDPRINT_MANIFEST, FilePath(FILE_PATH_LITERAL("cloud_print")));
382#endif
383
384#if defined(OS_CHROMEOS)
385  // Register access extensions only if accessibility is enabled.
386  if (local_state_->GetBoolean(prefs::kSpokenFeedbackEnabled)) {
387    FilePath path = FilePath(extension_misc::kAccessExtensionPath)
388        .AppendASCII(extension_misc::kChromeVoxDirectoryName);
389    Add(IDR_CHROMEVOX_MANIFEST, path);
390  }
391#endif
392
393  // If a URL for the enterprise webstore has been specified, load the
394  // component extension. This extension might also be loaded later, because
395  // it is specified by policy, and on ChromeOS policies are loaded after
396  // the browser process has started.
397  AddOrReloadEnterpriseWebStore();
398
399#if defined(USE_ASH)
400  AddChromeApp();
401#endif
402
403  AddScriptBubble();
404}
405
406void ComponentLoader::OnPreferenceChanged(PrefServiceBase* service,
407                                          const std::string& pref_name) {
408  DCHECK_EQ(std::string(prefs::kEnterpriseWebStoreURL), pref_name);
409  AddOrReloadEnterpriseWebStore();
410}
411
412// static
413void ComponentLoader::RegisterUserPrefs(PrefService* prefs) {
414  prefs->RegisterStringPref(prefs::kEnterpriseWebStoreURL,
415                            std::string() /* default_value */,
416                            PrefService::UNSYNCABLE_PREF);
417  prefs->RegisterStringPref(prefs::kEnterpriseWebStoreName,
418                            std::string() /* default_value */,
419                            PrefService::UNSYNCABLE_PREF);
420}
421
422}  // namespace extensions
423