blob: af187b15aff131893e59b78b882c051bb3093c0e [file] [log] [blame]
[email protected]1e8c93f2010-02-08 22:58:311// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]6014d672008-12-05 00:38:252// 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/extensions_service.h"
6
[email protected]654512b2010-09-01 02:09:427#include <algorithm>
8
[email protected]24b538a2010-02-27 01:22:449#include "base/basictypes.h"
[email protected]e2eb43112009-05-29 21:19:5410#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:2511#include "base/file_util.h"
[email protected]835d7c82010-10-14 04:38:3812#include "base/metrics/histogram.h"
[email protected]cd500f72010-06-25 23:44:3213#include "base/stl_util-inl.h"
[email protected]24b538a2010-02-27 01:22:4414#include "base/string16.h"
[email protected]e83326f2010-07-31 17:29:2515#include "base/string_number_conversions.h"
[email protected]6014d672008-12-05 00:38:2516#include "base/string_util.h"
[email protected]18d4b6c2010-09-21 03:21:0417#include "base/stringprintf.h"
[email protected]cc2c3432009-11-06 17:24:3618#include "base/time.h"
[email protected]ce7f62e32010-08-10 23:43:5919#include "base/utf_string_conversions.h"
[email protected]cc655912009-01-29 23:19:1920#include "base/values.h"
[email protected]aa142702010-03-26 01:26:3321#include "base/version.h"
[email protected]15730c42009-09-03 00:03:2022#include "chrome/browser/browser_process.h"
[email protected]ed7e6dd2010-10-12 02:02:4523#include "chrome/browser/browser_thread.h"
[email protected]4814b512009-11-07 00:12:2924#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5825#include "chrome/browser/extensions/crx_installer.h"
[email protected]ec5b50d2010-10-09 16:35:1826#include "chrome/browser/extensions/default_apps.h"
[email protected]5cbe1e22010-01-30 01:18:5627#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3828#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2829#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]2c5e1e12010-06-10 13:14:4430#include "chrome/browser/extensions/extension_cookies_api.h"
[email protected]c10da4b02010-03-25 14:38:3231#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0532#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]14a000d2010-04-29 21:44:2433#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]de768a832009-10-30 05:25:0134#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5635#include "chrome/browser/extensions/extension_host.h"
[email protected]7596ce72010-08-30 05:10:4636#include "chrome/browser/extensions/extension_management_api.h"
[email protected]4814b512009-11-07 00:12:2937#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1738#include "chrome/browser/extensions/extension_updater.h"
[email protected]784688a62010-09-13 07:06:5239#include "chrome/browser/extensions/extension_webnavigation_api.h"
[email protected]a1257b12009-06-12 02:51:3440#include "chrome/browser/extensions/external_extension_provider.h"
41#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]56ad3792010-05-28 17:45:3342#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]37858e52010-08-26 00:22:0243#include "chrome/browser/prefs/pref_service.h"
[email protected]81e63782009-02-27 19:35:0944#include "chrome/browser/profile.h"
[email protected]56ad3792010-05-28 17:45:3345#include "chrome/browser/search_engines/template_url_model.h"
[email protected]2a3e22b12010-08-13 04:55:1746#include "chrome/browser/sync/glue/extension_sync_traits.h"
[email protected]11edd1e2010-07-21 00:14:5047#include "chrome/browser/sync/glue/extension_util.h"
[email protected]aab98a52009-12-02 03:22:3548#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5449#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5850#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4051#include "chrome/common/extensions/extension_constants.h"
[email protected]05c82182010-06-24 17:49:0852#include "chrome/common/extensions/extension_error_utils.h"
[email protected]7c927b62010-02-24 09:54:1353#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0654#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2655#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2956#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3457#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1958#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1459#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3260#include "googleurl/src/gurl.h"
[email protected]1debbbb62010-10-06 17:23:4461#include "net/base/registry_controlled_domain.h"
[email protected]24b538a2010-02-27 01:22:4462#include "webkit/database/database_tracker.h"
63#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3164
[email protected]79db6232009-02-13 20:51:2065#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3466#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2067#endif
[email protected]6014d672008-12-05 00:38:2568
[email protected]5ef47ec2010-01-28 05:58:0569using base::Time;
70
[email protected]c6d474f82009-12-16 21:11:0671namespace errors = extension_manifest_errors;
72
[email protected]b6ab96d2009-08-20 18:58:1973namespace {
74
[email protected]29d0d4ac2010-09-08 21:10:3175#if defined(OS_LINUX)
76static const int kOmniboxIconPaddingLeft = 2;
77static const int kOmniboxIconPaddingRight = 2;
78#elif defined(OS_MACOSX)
79static const int kOmniboxIconPaddingLeft = 0;
80static const int kOmniboxIconPaddingRight = 2;
81#else
82static const int kOmniboxIconPaddingLeft = 0;
83static const int kOmniboxIconPaddingRight = 0;
84#endif
85
[email protected]c2c263c2010-08-13 21:59:4886bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
[email protected]2111b1a2010-03-12 18:12:4487 // Always reload LOAD extension manifests, because they can change on disk
88 // independent of the manifest in our prefs.
89 if (info.extension_location == Extension::LOAD)
90 return true;
91
92 // Otherwise, reload the manifest it needs to be relocalized.
93 return extension_l10n_util::ShouldRelocalizeManifest(info);
94}
95
[email protected]c2c263c2010-08-13 21:59:4896void GetExplicitOriginsInExtent(Extension* extension,
97 std::vector<GURL>* origins) {
98 typedef std::vector<URLPattern> PatternList;
99 std::set<GURL> set;
100 const PatternList& patterns = extension->web_extent().patterns();
101 for (PatternList::const_iterator pattern = patterns.begin();
102 pattern != patterns.end(); ++pattern) {
103 if (pattern->match_subdomains() || pattern->match_all_urls())
104 continue;
[email protected]654512b2010-09-01 02:09:42105 // Wildcard URL schemes won't parse into a valid GURL, so explicit schemes
106 // must be used.
107 PatternList explicit_patterns = pattern->ConvertToExplicitSchemes();
108 for (PatternList::const_iterator explicit_p = explicit_patterns.begin();
109 explicit_p != explicit_patterns.end(); ++explicit_p) {
110 GURL origin = GURL(explicit_p->GetAsString()).GetOrigin();
111 if (origin.is_valid()) {
112 set.insert(origin);
113 } else {
114 NOTREACHED();
115 }
116 }
[email protected]c2c263c2010-08-13 21:59:48117 }
118
119 for (std::set<GURL>::const_iterator unique = set.begin();
120 unique != set.end(); ++unique) {
121 origins->push_back(*unique);
122 }
123}
124
[email protected]c6d474f82009-12-16 21:11:06125} // namespace
[email protected]b6ab96d2009-08-20 18:58:19126
[email protected]8ef78fd2010-08-19 17:14:32127PendingExtensionInfo::PendingExtensionInfo(
128 const GURL& update_url,
129 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
130 bool is_from_sync,
131 bool install_silently,
132 bool enable_on_install,
[email protected]ec5b50d2010-10-09 16:35:18133 bool enable_incognito_on_install,
134 Extension::Location location)
[email protected]aa142702010-03-26 01:26:33135 : update_url(update_url),
[email protected]8ef78fd2010-08-19 17:14:32136 expected_crx_type(expected_crx_type),
137 is_from_sync(is_from_sync),
[email protected]4416c5a2010-06-26 01:28:57138 install_silently(install_silently),
139 enable_on_install(enable_on_install),
[email protected]ec5b50d2010-10-09 16:35:18140 enable_incognito_on_install(enable_incognito_on_install),
141 install_source(location) {}
[email protected]aa142702010-03-26 01:26:33142
143PendingExtensionInfo::PendingExtensionInfo()
144 : update_url(),
[email protected]8ef78fd2010-08-19 17:14:32145 expected_crx_type(PendingExtensionInfo::UNKNOWN),
146 is_from_sync(true),
[email protected]4416c5a2010-06-26 01:28:57147 install_silently(false),
148 enable_on_install(false),
[email protected]ec5b50d2010-10-09 16:35:18149 enable_incognito_on_install(false),
150 install_source(Extension::INVALID) {}
[email protected]aa142702010-03-26 01:26:33151
[email protected]25b34332009-06-05 21:53:19152// ExtensionsService.
[email protected]6014d672008-12-05 00:38:25153
[email protected]cc655912009-01-29 23:19:19154const char* ExtensionsService::kInstallDirectoryName = "Extensions";
155const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42156
[email protected]7a4c6852010-09-16 03:44:22157// Implements IO for the ExtensionsService.
158
159class ExtensionsServiceBackend
160 : public base::RefCountedThreadSafe<ExtensionsServiceBackend>,
161 public ExternalExtensionProvider::Visitor {
162 public:
[email protected]1f830eb2010-09-28 08:25:14163 // |install_directory| is a path where to look for extensions to load.
164 // |load_external_extensions| indicates whether or not backend should load
165 // external extensions listed in JSON file and Windows registry.
166 ExtensionsServiceBackend(const FilePath& install_directory,
167 bool load_external_extensions);
[email protected]7a4c6852010-09-16 03:44:22168
169 // Loads a single extension from |path| where |path| is the top directory of
170 // a specific extension where its manifest file lives.
171 // Errors are reported through ExtensionErrorReporter. On success,
172 // OnExtensionLoaded() is called.
173 // TODO(erikkay): It might be useful to be able to load a packed extension
174 // (presumably into memory) without installing it.
175 void LoadSingleExtension(const FilePath &path,
176 scoped_refptr<ExtensionsService> frontend);
177
178 // Check externally updated extensions for updates and install if necessary.
179 // Errors are reported through ExtensionErrorReporter. Succcess is not
180 // reported.
181 void CheckForExternalUpdates(std::set<std::string> ids_to_ignore,
182 scoped_refptr<ExtensionsService> frontend);
183
184 // For the extension in |version_path| with |id|, check to see if it's an
185 // externally managed extension. If so, tell the frontend to uninstall it.
186 void CheckExternalUninstall(scoped_refptr<ExtensionsService> frontend,
187 const std::string& id,
188 Extension::Location location);
189
190 // Clear all ExternalExtensionProviders.
191 void ClearProvidersForTesting();
192
193 // Sets an ExternalExtensionProvider for the service to use during testing.
194 // |location| specifies what type of provider should be added.
195 void SetProviderForTesting(Extension::Location location,
196 ExternalExtensionProvider* test_provider);
197
198 // ExternalExtensionProvider::Visitor implementation.
199 virtual void OnExternalExtensionFileFound(const std::string& id,
200 const Version* version,
201 const FilePath& path,
202 Extension::Location location);
203
[email protected]a424d84c2010-09-24 09:31:15204 virtual void OnExternalExtensionUpdateUrlFound(const std::string& id,
205 const GURL& update_url);
[email protected]7a4c6852010-09-16 03:44:22206
207 // Reloads the given extensions from their manifests on disk (instead of what
208 // we have cached in the prefs).
209 void ReloadExtensionManifests(
210 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
211 base::TimeTicks start_time,
212 scoped_refptr<ExtensionsService> frontend);
213
214 private:
215 friend class base::RefCountedThreadSafe<ExtensionsServiceBackend>;
216
217 virtual ~ExtensionsServiceBackend();
218
219 // Finish installing the extension in |crx_path| after it has been unpacked to
220 // |unpacked_path|. If |expected_id| is not empty, it's verified against the
221 // extension's manifest before installation. If |silent| is true, there will
222 // be no install confirmation dialog. |from_gallery| indicates whether the
223 // crx was installed from our gallery, which results in different UI.
224 //
225 // Note: We take ownership of |extension|.
226 void OnExtensionUnpacked(const FilePath& crx_path,
227 const FilePath& unpacked_path,
228 Extension* extension,
229 const std::string expected_id);
230
231 // Notify the frontend that there was an error loading an extension.
232 void ReportExtensionLoadError(const FilePath& extension_path,
233 const std::string& error);
234
235 // Lookup an external extension by |id| by going through all registered
236 // external extension providers until we find a provider that contains an
237 // extension that matches. If |version| is not NULL, the extension version
238 // will be returned (caller is responsible for deleting that pointer).
239 // |location| can also be null, if not needed. Returns true if extension is
240 // found, false otherwise.
241 bool LookupExternalExtension(const std::string& id,
242 Version** version,
243 Extension::Location* location);
244
245 // This is a naked pointer which is set by each entry point.
246 // The entry point is responsible for ensuring lifetime.
247 ExtensionsService* frontend_;
248
249 // The top-level extensions directory being installed to.
250 FilePath install_directory_;
251
252 // Whether errors result in noisy alerts.
253 bool alert_on_error_;
254
[email protected]55196e92010-09-29 15:04:46255 // A map from external extension type to the external extension provider
256 // for that type. Because a single provider may handle more than one
257 // external extension type, more than one key may map to the same object.
[email protected]7a4c6852010-09-16 03:44:22258 typedef std::map<Extension::Location,
259 linked_ptr<ExternalExtensionProvider> > ProviderMap;
260 ProviderMap external_extension_providers_;
261
262 // Set to true by OnExternalExtensionUpdateUrlFound() when an external
263 // extension URL is found. Used in CheckForExternalUpdates() to see
264 // if an update check is needed to install pending extensions.
265 bool external_extension_added_;
266
267 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceBackend);
268};
269
270ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]1f830eb2010-09-28 08:25:14271 const FilePath& install_directory,
272 bool load_external_extensions)
[email protected]7a4c6852010-09-16 03:44:22273 : frontend_(NULL),
274 install_directory_(install_directory),
275 alert_on_error_(false),
276 external_extension_added_(false) {
[email protected]1f830eb2010-09-28 08:25:14277 if (!load_external_extensions)
278 return;
279
[email protected]7a4c6852010-09-16 03:44:22280 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
281 // pref data in the ctor and that is called on the UI thread. Would be better
282 // to re-read data each time we list external extensions, anyway.
283 external_extension_providers_[Extension::EXTERNAL_PREF] =
284 linked_ptr<ExternalExtensionProvider>(
285 new ExternalPrefExtensionProvider());
[email protected]55196e92010-09-29 15:04:46286 // EXTERNAL_PREF_DOWNLOAD and EXTERNAL_PREF extensions are handled by the
287 // same object.
288 external_extension_providers_[Extension::EXTERNAL_PREF_DOWNLOAD] =
289 external_extension_providers_[Extension::EXTERNAL_PREF];
[email protected]7a4c6852010-09-16 03:44:22290#if defined(OS_WIN)
291 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
292 linked_ptr<ExternalExtensionProvider>(
293 new ExternalRegistryExtensionProvider());
294#endif
295}
296
297ExtensionsServiceBackend::~ExtensionsServiceBackend() {
298}
299
300void ExtensionsServiceBackend::LoadSingleExtension(
301 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
302 frontend_ = frontend;
303
304 // Explicit UI loads are always noisy.
305 alert_on_error_ = true;
306
307 FilePath extension_path = path_in;
308 file_util::AbsolutePath(&extension_path);
309
[email protected]7a4c6852010-09-16 03:44:22310 std::string error;
311 Extension* extension = extension_file_util::LoadExtension(
312 extension_path,
[email protected]92888082010-10-18 19:24:57313 Extension::LOAD,
[email protected]7a4c6852010-09-16 03:44:22314 false, // Don't require id
315 &error);
316
317 if (!extension) {
318 ReportExtensionLoadError(extension_path, error);
319 return;
320 }
321
[email protected]7a4c6852010-09-16 03:44:22322 // Report this as an installed extension so that it gets remembered in the
323 // prefs.
[email protected]ca4b5fa32010-10-09 12:42:18324 BrowserThread::PostTask(
325 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22326 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
327 extension, true));
328}
329
330void ExtensionsServiceBackend::ReportExtensionLoadError(
331 const FilePath& extension_path, const std::string &error) {
[email protected]ca4b5fa32010-10-09 12:42:18332 BrowserThread::PostTask(
333 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22334 NewRunnableMethod(
335 frontend_,
336 &ExtensionsService::ReportExtensionLoadError, extension_path,
337 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
338}
339
340bool ExtensionsServiceBackend::LookupExternalExtension(
341 const std::string& id, Version** version, Extension::Location* location) {
342 scoped_ptr<Version> extension_version;
343 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
344 i != external_extension_providers_.end(); ++i) {
345 const ExternalExtensionProvider* provider = i->second.get();
346 extension_version.reset(provider->RegisteredVersion(id, location));
347 if (extension_version.get()) {
348 if (version)
349 *version = extension_version.release();
350 return true;
351 }
352 }
353 return false;
354}
355
356// Some extensions will autoupdate themselves externally from Chrome. These
357// are typically part of some larger client application package. To support
358// these, the extension will register its location in the the preferences file
359// (and also, on Windows, in the registry) and this code will periodically
360// check that location for a .crx file, which it will then install locally if
361// a new version is available.
362void ExtensionsServiceBackend::CheckForExternalUpdates(
363 std::set<std::string> ids_to_ignore,
364 scoped_refptr<ExtensionsService> frontend) {
365 // Note that this installation is intentionally silent (since it didn't
366 // go through the front-end). Extensions that are registered in this
367 // way are effectively considered 'pre-bundled', and so implicitly
368 // trusted. In general, if something has HKLM or filesystem access,
369 // they could install an extension manually themselves anyway.
370 alert_on_error_ = false;
371 frontend_ = frontend;
372 external_extension_added_ = false;
373
374 // Ask each external extension provider to give us a call back for each
375 // extension they know about. See OnExternalExtension(File|UpdateUrl)Found.
376
377 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
378 i != external_extension_providers_.end(); ++i) {
379 ExternalExtensionProvider* provider = i->second.get();
380 provider->VisitRegisteredExtension(this, ids_to_ignore);
381 }
382
383 if (external_extension_added_ && frontend->updater()) {
[email protected]ca4b5fa32010-10-09 12:42:18384 BrowserThread::PostTask(
385 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22386 NewRunnableMethod(
387 frontend->updater(), &ExtensionUpdater::CheckNow));
388 }
389}
390
391void ExtensionsServiceBackend::CheckExternalUninstall(
392 scoped_refptr<ExtensionsService> frontend, const std::string& id,
393 Extension::Location location) {
394 // Check if the providers know about this extension.
395 ProviderMap::const_iterator i = external_extension_providers_.find(location);
396 if (i == external_extension_providers_.end()) {
397 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
398 << location;
399 return;
400 }
401
402 scoped_ptr<Version> version;
403 version.reset(i->second->RegisteredVersion(id, NULL));
404 if (version.get())
405 return; // Yup, known extension, don't uninstall.
406
407 // This is an external extension that we don't have registered. Uninstall.
[email protected]ca4b5fa32010-10-09 12:42:18408 BrowserThread::PostTask(
409 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22410 NewRunnableMethod(
411 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
412}
413
414void ExtensionsServiceBackend::ClearProvidersForTesting() {
415 external_extension_providers_.clear();
416}
417
418void ExtensionsServiceBackend::SetProviderForTesting(
419 Extension::Location location,
420 ExternalExtensionProvider* test_provider) {
421 DCHECK(test_provider);
422 external_extension_providers_[location] =
423 linked_ptr<ExternalExtensionProvider>(test_provider);
424}
425
426void ExtensionsServiceBackend::OnExternalExtensionFileFound(
427 const std::string& id, const Version* version, const FilePath& path,
428 Extension::Location location) {
429 DCHECK(version);
[email protected]ca4b5fa32010-10-09 12:42:18430 BrowserThread::PostTask(
431 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22432 NewRunnableMethod(
433 frontend_, &ExtensionsService::OnExternalExtensionFileFound, id,
434 version->GetString(), path, location));
435}
436
437void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound(
438 const std::string& id,
[email protected]a424d84c2010-09-24 09:31:15439 const GURL& update_url) {
[email protected]7a4c6852010-09-16 03:44:22440 if (frontend_->GetExtensionById(id, true)) {
441 // Already installed. Do not change the update URL that the extension set.
442 return;
443 }
444
[email protected]a424d84c2010-09-24 09:31:15445 frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url);
[email protected]7a4c6852010-09-16 03:44:22446 external_extension_added_ |= true;
447}
448
449void ExtensionsServiceBackend::ReloadExtensionManifests(
450 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
451 base::TimeTicks start_time,
452 scoped_refptr<ExtensionsService> frontend) {
453 frontend_ = frontend;
454
455 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
456 ExtensionInfo* info = extensions_to_reload->at(i).get();
457 if (!ShouldReloadExtensionManifest(*info))
458 continue;
459
460 // We need to reload original manifest in order to localize properly.
461 std::string error;
462 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
[email protected]92888082010-10-18 19:24:57463 info->extension_path, info->extension_location, false, &error));
[email protected]7a4c6852010-09-16 03:44:22464
465 if (extension.get())
466 extensions_to_reload->at(i)->extension_manifest.reset(
467 static_cast<DictionaryValue*>(
468 extension->manifest_value()->DeepCopy()));
469 }
470
471 // Finish installing on UI thread.
[email protected]ca4b5fa32010-10-09 12:42:18472 BrowserThread::PostTask(
473 BrowserThread::UI, FROM_HERE,
[email protected]7a4c6852010-09-16 03:44:22474 NewRunnableMethod(
475 frontend_,
476 &ExtensionsService::ContinueLoadAllExtensions,
477 extensions_to_reload,
478 start_time,
479 true));
480}
481
[email protected]334e04a2010-06-24 23:34:44482bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
483 const GURL& referrer_url) {
[email protected]d3071992010-10-08 15:24:07484 // Special-case the themes mini-gallery.
485 // TODO(erikkay) When that gallery goes away, remove this code.
486 if (IsDownloadFromMiniGallery(download_url) &&
487 StartsWithASCII(referrer_url.spec(),
488 extension_urls::kMiniGalleryBrowsePrefix, false)) {
[email protected]334e04a2010-06-24 23:34:44489 return true;
[email protected]1debbbb62010-10-06 17:23:44490 }
[email protected]473ff6e2010-05-12 15:31:55491
[email protected]d3071992010-10-08 15:24:07492 Extension* download_extension = GetExtensionByWebExtent(download_url);
493 Extension* referrer_extension = GetExtensionByWebExtent(referrer_url);
494 Extension* webstore_app = GetWebStoreApp();
495
496 bool referrer_valid = (referrer_extension == webstore_app);
497 bool download_valid = (download_extension == webstore_app);
498
499 // If the command-line gallery URL is set, then be a bit more lenient.
500 GURL store_url =
501 GURL(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
502 switches::kAppsGalleryURL));
503 if (!store_url.is_empty()) {
504 std::string store_tld =
505 net::RegistryControlledDomainService::GetDomainAndRegistry(store_url);
506 if (!referrer_valid) {
507 std::string referrer_tld =
508 net::RegistryControlledDomainService::GetDomainAndRegistry(
509 referrer_url);
510 // The referrer gets stripped when transitioning from https to http,
511 // or when hitting an unknown test cert and that commonly happens in
512 // testing environments. Given this, we allow an empty referrer when
513 // the command-line flag is set.
514 // Otherwise, the TLD must match the TLD of the command-line url.
515 referrer_valid = referrer_url.is_empty() || (referrer_tld == store_tld);
516 }
517
518 if (!download_valid) {
519 std::string download_tld =
520 net::RegistryControlledDomainService::GetDomainAndRegistry(
521 GURL(download_url));
522
523 // Otherwise, the TLD must match the TLD of the command-line url.
524 download_valid = (download_tld == store_tld);
525 }
526 }
527
528 return (referrer_valid && download_valid);
[email protected]b7c2f252009-12-08 00:47:23529}
530
[email protected]ac025282009-12-16 19:16:38531bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
532 return StartsWithASCII(download_url.spec(),
533 extension_urls::kMiniGalleryDownloadPrefix,
534 false); // case_sensitive
535}
536
[email protected]6aeac8342010-10-01 20:21:18537// static
538bool ExtensionsService::UninstallExtensionHelper(
539 ExtensionsService* extensions_service,
540 const std::string& extension_id) {
541 DCHECK(extensions_service);
542
543 // We can't call UninstallExtension with an invalid extension ID, so check it
544 // first.
545 if (extensions_service->GetExtensionById(extension_id, true)) {
546 extensions_service->UninstallExtension(extension_id, false);
547 } else {
548 LOG(WARNING) << "Attempted uninstallation of non-existent extension with "
549 << "extension with id: " << extension_id;
550 return false;
551 }
552
553 return true;
554}
555
[email protected]81e63782009-02-27 19:35:09556ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08557 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23558 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17559 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12560 : profile_(profile),
[email protected]2fb7dc982010-09-29 12:24:28561 extension_prefs_(new ExtensionPrefs(profile->GetPrefs(),
562 install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23563 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23564 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13565 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31566 ready_(false),
[email protected]ec5b50d2010-10-09 16:35:18567 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)),
[email protected]b3d62312b12010-10-14 21:10:18568 default_apps_(profile->GetPrefs()),
569 event_routers_initialized_(false) {
[email protected]36a784c2009-06-23 06:21:08570 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23571 if (command_line->HasSwitch(switches::kDisableExtensions)) {
572 extensions_enabled_ = false;
573 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
574 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18575 }
[email protected]36a784c2009-06-23 06:21:08576
[email protected]a4ed6282009-12-14 20:51:16577 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]bc535ee52010-08-31 18:40:32578 NotificationService::AllSources());
[email protected]2fb7dc982010-09-29 12:24:28579 pref_change_registrar_.Init(profile->GetPrefs());
580 pref_change_registrar_.Add(prefs::kExtensionInstallAllowList, this);
581 pref_change_registrar_.Add(prefs::kExtensionInstallDenyList, this);
[email protected]4814b512009-11-07 00:12:29582
[email protected]93fd78f42009-07-10 16:43:17583 // Set up the ExtensionUpdater
584 if (autoupdate_enabled) {
585 int update_frequency = kDefaultUpdateFrequencySeconds;
586 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]e83326f2010-07-31 17:29:25587 base::StringToInt(command_line->GetSwitchValueASCII(
588 switches::kExtensionsUpdateFrequency),
589 &update_frequency);
[email protected]93fd78f42009-07-10 16:43:17590 }
[email protected]2fb7dc982010-09-29 12:24:28591 updater_ = new ExtensionUpdater(this,
592 profile->GetPrefs(),
593 update_frequency);
[email protected]93fd78f42009-07-10 16:43:17594 }
595
[email protected]1f830eb2010-09-28 08:25:14596 backend_ = new ExtensionsServiceBackend(install_directory_,
597 extensions_enabled_);
[email protected]b671760b2010-07-15 21:13:47598
[email protected]aa96d3a2010-08-21 08:45:25599 // Use monochrome icons for Omnibox icons.
[email protected]29d0d4ac2010-09-08 21:10:31600 omnibox_popup_icon_manager_.set_monochrome(true);
[email protected]b671760b2010-07-15 21:13:47601 omnibox_icon_manager_.set_monochrome(true);
[email protected]29d0d4ac2010-09-08 21:10:31602 omnibox_icon_manager_.set_padding(gfx::Insets(0, kOmniboxIconPaddingLeft,
603 0, kOmniboxIconPaddingRight));
[email protected]6014d672008-12-05 00:38:25604}
605
606ExtensionsService::~ExtensionsService() {
[email protected]2fb7dc982010-09-29 12:24:28607 DCHECK(!profile_); // Profile should have told us it's going away.
[email protected]9f1087e2009-06-15 17:29:32608 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17609 if (updater_.get()) {
610 updater_->Stop();
611 }
[email protected]6014d672008-12-05 00:38:25612}
613
[email protected]c5ae74ab2010-04-15 18:14:37614void ExtensionsService::InitEventRouters() {
[email protected]b3d62312b12010-10-14 21:10:18615 if (event_routers_initialized_)
616 return;
617
[email protected]c5ae74ab2010-04-15 18:14:37618 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
619 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]56ee0152010-06-16 01:54:42620 ExtensionBrowserEventRouter::GetInstance()->Init(profile_);
[email protected]c5ae74ab2010-04-15 18:14:37621 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
622 profile_->GetBookmarkModel());
[email protected]2c5e1e12010-06-10 13:14:44623 ExtensionCookiesEventRouter::GetInstance()->Init();
[email protected]7596ce72010-08-30 05:10:46624 ExtensionManagementEventRouter::GetInstance()->Init();
[email protected]784688a62010-09-13 07:06:52625 ExtensionWebNavigationEventRouter::GetInstance()->Init();
[email protected]b3d62312b12010-10-14 21:10:18626 event_routers_initialized_ = true;
[email protected]c5ae74ab2010-04-15 18:14:37627}
628
[email protected]9f1087e2009-06-15 17:29:32629void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29630 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17631 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32632
[email protected]95dd38f2009-10-20 20:09:15633 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
634 // the first extension, because its members listen for loaded notifications.
635 g_browser_process->resource_dispatcher_host();
636
[email protected]9f1087e2009-06-15 17:29:32637 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57638
[email protected]9f1087e2009-06-15 17:29:32639 // TODO(erikkay) this should probably be deferred to a future point
640 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17641 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57642
[email protected]9f1087e2009-06-15 17:29:32643 // TODO(erikkay) this should probably be deferred as well.
644 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25645}
646
[email protected]3cf4f0992009-02-03 23:00:30647void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16648 scoped_refptr<CrxInstaller> installer(
649 new CrxInstaller(install_directory_,
650 this, // frontend
651 NULL)); // no client (silent install)
652 installer->set_allow_privilege_increase(true);
653 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30654}
655
[email protected]aa142702010-03-26 01:26:33656namespace {
657 // TODO(akalin): Put this somewhere where both crx_installer.cc and
658 // this file can use it.
659 void DeleteFileHelper(const FilePath& path, bool recursive) {
[email protected]ca4b5fa32010-10-09 12:42:18660 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
[email protected]aa142702010-03-26 01:26:33661 file_util::Delete(path, recursive);
662 }
663} // namespace
664
[email protected]e957fe52009-06-23 16:51:05665void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34666 const FilePath& extension_path,
667 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33668 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
[email protected]8ef78fd2010-08-19 17:14:32669 bool is_pending_extension = (it != pending_extensions_.end());
670
671 if (!is_pending_extension &&
[email protected]aa142702010-03-26 01:26:33672 !GetExtensionByIdInternal(id, true, true)) {
673 LOG(WARNING) << "Will not update extension " << id
674 << " because it is not installed or pending";
675 // Delete extension_path since we're not creating a CrxInstaller
676 // that would do it for us.
[email protected]ca4b5fa32010-10-09 12:42:18677 BrowserThread::PostTask(
678 BrowserThread::FILE, FROM_HERE,
[email protected]aa142702010-03-26 01:26:33679 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49680 return;
[email protected]e957fe52009-06-23 16:51:05681 }
682
[email protected]aa142702010-03-26 01:26:33683 // We want a silent install only for non-pending extensions and
684 // pending extensions that have install_silently set.
685 ExtensionInstallUI* client =
[email protected]8ef78fd2010-08-19 17:14:32686 (!is_pending_extension || it->second.install_silently) ?
[email protected]aa142702010-03-26 01:26:33687 NULL : new ExtensionInstallUI(profile_);
688
[email protected]6dfbbf82010-03-12 23:09:16689 scoped_refptr<CrxInstaller> installer(
690 new CrxInstaller(install_directory_,
691 this, // frontend
[email protected]aa142702010-03-26 01:26:33692 client));
[email protected]6dfbbf82010-03-12 23:09:16693 installer->set_expected_id(id);
[email protected]ec5b50d2010-10-09 16:35:18694 if (is_pending_extension)
695 installer->set_install_source(it->second.install_source);
[email protected]6dfbbf82010-03-12 23:09:16696 installer->set_delete_source(true);
[email protected]5c8516202010-03-18 21:43:34697 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16698 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05699}
700
[email protected]8ef78fd2010-08-19 17:14:32701void ExtensionsService::AddPendingExtensionFromSync(
[email protected]aa142702010-03-26 01:26:33702 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32703 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
704 bool install_silently, bool enable_on_install,
705 bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33706 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25707 LOG(DFATAL) << "Trying to add pending extension " << id
708 << " which already exists";
[email protected]aa142702010-03-26 01:26:33709 return;
710 }
[email protected]ec5b50d2010-10-09 16:35:18711
712 AddPendingExtensionInternal(id, update_url, expected_crx_type, true,
713 install_silently, enable_on_install,
714 enable_incognito_on_install,
715 Extension::INTERNAL);
[email protected]aa142702010-03-26 01:26:33716}
717
[email protected]8ef78fd2010-08-19 17:14:32718void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl(
[email protected]a424d84c2010-09-24 09:31:15719 const std::string& id, const GURL& update_url) {
[email protected]8ef78fd2010-08-19 17:14:32720 // Add the extension to this list of extensions to update.
[email protected]8ef78fd2010-08-19 17:14:32721 const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType =
722 PendingExtensionInfo::UNKNOWN;
723 const bool kIsFromSync = false;
724 const bool kInstallSilently = true;
725 const bool kEnableOnInstall = true;
[email protected]a424d84c2010-09-24 09:31:15726 const bool kEnableIncognitoOnInstall = false;
[email protected]8ef78fd2010-08-19 17:14:32727
728 if (GetExtensionByIdInternal(id, true, true)) {
729 LOG(DFATAL) << "Trying to add extension " << id
730 << " by external update, but it is already installed.";
731 return;
732 }
733
734 AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync,
735 kInstallSilently, kEnableOnInstall,
[email protected]ec5b50d2010-10-09 16:35:18736 kEnableIncognitoOnInstall,
737 Extension::EXTERNAL_PREF_DOWNLOAD);
738}
739
740void ExtensionsService::AddPendingExtensionFromDefaultAppList(
741 const std::string& id) {
742 // Add the extension to this list of extensions to update.
743 const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType =
744 PendingExtensionInfo::APP;
745 const bool kIsFromSync = false;
746 const bool kInstallSilently = true;
747 const bool kEnableOnInstall = true;
748 const bool kEnableIncognitoOnInstall = true;
749
750 // This can legitimately happen if the user manually installed one of the
751 // default apps before this code ran.
752 if (GetExtensionByIdInternal(id, true, true))
753 return;
754
755 AddPendingExtensionInternal(id, GURL(), kExpectedCrxType, kIsFromSync,
756 kInstallSilently, kEnableOnInstall,
757 kEnableIncognitoOnInstall,
758 Extension::INTERNAL);
[email protected]8ef78fd2010-08-19 17:14:32759}
760
[email protected]aa142702010-03-26 01:26:33761void ExtensionsService::AddPendingExtensionInternal(
762 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32763 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
764 bool is_from_sync, bool install_silently,
[email protected]ec5b50d2010-10-09 16:35:18765 bool enable_on_install, bool enable_incognito_on_install,
766 Extension::Location install_source) {
[email protected]aa142702010-03-26 01:26:33767 pending_extensions_[id] =
[email protected]8ef78fd2010-08-19 17:14:32768 PendingExtensionInfo(update_url, expected_crx_type, is_from_sync,
769 install_silently, enable_on_install,
[email protected]ec5b50d2010-10-09 16:35:18770 enable_incognito_on_install, install_source);
[email protected]aa142702010-03-26 01:26:33771}
772
[email protected]9cddd4702009-07-27 22:09:40773void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06774 FilePath path;
[email protected]61b411612009-11-10 23:17:41775 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40776
[email protected]f17dbd42010-08-16 23:21:10777 // Disable the extension if it's loaded. It might not be loaded if it crashed.
[email protected]b65272f2009-08-31 15:47:06778 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29779 // If the extension has an inspector open for its background page, detach
780 // the inspector and hang onto a cookie for it, so that we can reattach
781 // later.
782 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
783 ExtensionHost* host = manager->GetBackgroundHostForExtension(
784 current_extension);
785 if (host) {
786 // Look for an open inspector for the background page.
787 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
788 host->render_view_host());
789 if (devtools_cookie >= 0)
790 orphaned_dev_tools_[extension_id] = devtools_cookie;
791 }
792
[email protected]b65272f2009-08-31 15:47:06793 path = current_extension->path();
[email protected]f17dbd42010-08-16 23:21:10794 DisableExtension(extension_id);
795 disabled_extension_paths_[extension_id] = path;
[email protected]1eb175082010-02-10 09:26:16796 } else {
797 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06798 }
799
[email protected]e6090e42010-03-23 22:44:08800 // Check the installed extensions to see if what we're reloading was already
801 // installed.
802 scoped_ptr<ExtensionInfo> installed_extension(
803 extension_prefs_->GetInstalledExtensionInfo(extension_id));
804 if (installed_extension.get() &&
805 installed_extension->extension_manifest.get()) {
806 LoadInstalledExtension(*installed_extension, false);
807 } else {
808 // We should always be able to remember the extension's path. If it's not in
809 // the map, someone failed to update |unloaded_extension_paths_|.
810 CHECK(!path.empty());
811 LoadExtension(path);
812 }
[email protected]9cddd4702009-07-27 22:09:40813}
814
[email protected]27b985d2009-06-25 17:53:15815void ExtensionsService::UninstallExtension(const std::string& extension_id,
816 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39817 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25818
[email protected]e7afe2452010-08-22 16:19:13819 // Callers should not send us nonexistent extensions.
[email protected]e72e8eb82009-06-18 17:21:51820 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32821
[email protected]831aa212010-03-26 13:55:19822 // Get hold of information we need after unloading, since the extension
823 // pointer will be invalid then.
824 GURL extension_url(extension->url());
825 Extension::Location location(extension->location());
[email protected]211030342010-09-30 18:41:06826 UninstalledExtensionInfo uninstalled_extension_info(*extension);
[email protected]831aa212010-03-26 13:55:19827
[email protected]9b217652010-10-08 22:04:23828 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallType",
829 extension->GetHistogramType(), 100);
830
[email protected]831aa212010-03-26 13:55:19831 // Also copy the extension identifier since the reference might have been
832 // obtained via Extension::id().
833 std::string extension_id_copy(extension_id);
834
[email protected]56ad3792010-05-28 17:45:33835 if (profile_->GetTemplateURLModel())
836 profile_->GetTemplateURLModel()->UnregisterExtensionKeyword(extension);
837
[email protected]831aa212010-03-26 13:55:19838 // Unload before doing more cleanup to ensure that nothing is hanging on to
839 // any of these resources.
840 UnloadExtension(extension_id);
841
842 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
843 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32844
845 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19846 if (Extension::LOAD != location) {
[email protected]ca4b5fa32010-10-09 12:42:18847 BrowserThread::PostTask(
848 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:06849 NewRunnableFunction(
[email protected]ca3dbf52010-05-19 22:27:06850 &extension_file_util::UninstallExtension,
851 install_directory_,
852 extension_id_copy));
[email protected]9f1087e2009-06-15 17:29:32853 }
854
[email protected]c10da4b02010-03-25 14:38:32855 ClearExtensionData(extension_url);
[email protected]211030342010-09-30 18:41:06856
857 // Notify interested parties that we've uninstalled this extension.
858 NotificationService::current()->Notify(
859 NotificationType::EXTENSION_UNINSTALLED,
860 Source<Profile>(profile_),
861 Details<UninstalledExtensionInfo>(&uninstalled_extension_info));
[email protected]c10da4b02010-03-25 14:38:32862}
863
864void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
865 scoped_refptr<ExtensionDataDeleter> deleter(
866 new ExtensionDataDeleter(profile_, extension_url));
867 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32868}
869
[email protected]0c6da502009-08-14 22:32:39870void ExtensionsService::EnableExtension(const std::string& extension_id) {
871 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
872 if (!extension) {
[email protected]0c6da502009-08-14 22:32:39873 return;
874 }
875
[email protected]e8c729a2010-03-09 19:55:19876 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52877
[email protected]0c6da502009-08-14 22:32:39878 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39879 extensions_.push_back(extension);
880 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
881 disabled_extensions_.end(),
882 extension);
883 disabled_extensions_.erase(iter);
884
[email protected]86c008e82009-08-28 20:26:05885 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
886 extension->GetChromeURLOverrides());
887
[email protected]62d30f42009-10-01 22:36:06888 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35889 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39890}
891
[email protected]1784e83a2009-09-08 21:01:52892void ExtensionsService::DisableExtension(const std::string& extension_id) {
893 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15894 // The extension may have been disabled already.
895 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52896 return;
[email protected]1784e83a2009-09-08 21:01:52897
[email protected]e8c729a2010-03-09 19:55:19898 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52899
900 // Move it over to the disabled list.
901 disabled_extensions_.push_back(extension);
902 ExtensionList::iterator iter = std::find(extensions_.begin(),
903 extensions_.end(),
904 extension);
905 extensions_.erase(iter);
906
907 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
908 extension->GetChromeURLOverrides());
909
[email protected]62d30f42009-10-01 22:36:06910 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35911 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52912}
913
[email protected]9f1087e2009-06-15 17:29:32914void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]ca4b5fa32010-10-09 12:42:18915 BrowserThread::PostTask(
916 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:06917 NewRunnableMethod(
918 backend_.get(),
919 &ExtensionsServiceBackend::LoadSingleExtension,
920 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32921}
922
[email protected]1952c7d2010-03-04 23:48:34923void ExtensionsService::LoadComponentExtensions() {
924 for (RegisteredComponentExtensions::iterator it =
925 component_extension_manifests_.begin();
926 it != component_extension_manifests_.end(); ++it) {
927 JSONStringValueSerializer serializer(it->manifest);
[email protected]ba399672010-04-06 15:42:39928 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
[email protected]999731f2010-03-22 19:13:53929 if (!manifest.get()) {
[email protected]a94d57d2010-09-13 22:53:00930 DLOG(ERROR) << "Failed to parse manifest for extension";
[email protected]999731f2010-03-22 19:13:53931 continue;
932 }
[email protected]1952c7d2010-03-04 23:48:34933
934 scoped_ptr<Extension> extension(new Extension(it->root_directory));
935 extension->set_location(Extension::COMPONENT);
936
937 std::string error;
938 if (!extension->InitFromValue(
939 *static_cast<DictionaryValue*>(manifest.get()),
940 true, // require key
941 &error)) {
[email protected]4fdbc1492010-07-01 01:20:59942 NOTREACHED() << error;
[email protected]1952c7d2010-03-04 23:48:34943 return;
944 }
945
[email protected]1952c7d2010-03-04 23:48:34946 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
947 // increase.
948 }
949}
950
[email protected]9f1087e2009-06-15 17:29:32951void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36952 base::TimeTicks start_time = base::TimeTicks::Now();
953
[email protected]1952c7d2010-03-04 23:48:34954 // Load any component extensions.
955 LoadComponentExtensions();
956
[email protected]e72e8eb82009-06-18 17:21:51957 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06958 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08959 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06960
961 // If any extensions need localization, we bounce them all to the file thread
962 // for re-reading and localization.
963 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44964 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]ca4b5fa32010-10-09 12:42:18965 BrowserThread::PostTask(
966 BrowserThread::FILE, FROM_HERE, NewRunnableMethod(
[email protected]c6d474f82009-12-16 21:11:06967 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44968 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06969 info.release(), // Callee takes ownership of the memory.
970 start_time,
971 scoped_refptr<ExtensionsService>(this)));
972 return;
973 }
974 }
975
976 // Don't update prefs.
977 // Callee takes ownership of the memory.
978 ContinueLoadAllExtensions(info.release(), start_time, false);
979}
980
981void ExtensionsService::ContinueLoadAllExtensions(
982 ExtensionPrefs::ExtensionsInfo* extensions_info,
983 base::TimeTicks start_time,
984 bool write_to_prefs) {
985 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
986
987 for (size_t i = 0; i < info->size(); ++i) {
988 LoadInstalledExtension(*info->at(i), write_to_prefs);
989 }
990
[email protected]ae09ca62009-08-21 19:46:46991 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36992
993 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
994 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
995
[email protected]1952c7d2010-03-04 23:48:34996 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
997 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36998
[email protected]9b217652010-10-08 22:04:23999 int app_count = 0;
1000 int hosted_app_count = 0;
1001 int packaged_app_count = 0;
[email protected]1952c7d2010-03-04 23:48:341002 int user_script_count = 0;
1003 int extension_count = 0;
1004 int theme_count = 0;
1005 int external_count = 0;
1006 int page_action_count = 0;
1007 int browser_action_count = 0;
1008 ExtensionList::iterator ex;
1009 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
[email protected]9b217652010-10-08 22:04:231010 Extension::Location location = (*ex)->location();
1011 Extension::HistogramType type = (*ex)->GetHistogramType();
1012 if ((*ex)->is_app()) {
1013 UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation",
1014 location, 100);
1015 } else if (type == Extension::TYPE_EXTENSION) {
1016 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation",
1017 location, 100);
1018 }
1019
[email protected]1952c7d2010-03-04 23:48:341020 // Don't count component extensions, since they are only extensions as an
1021 // implementation detail.
[email protected]9b217652010-10-08 22:04:231022 if (location == Extension::COMPONENT)
[email protected]1952c7d2010-03-04 23:48:341023 continue;
1024
[email protected]e8c729a2010-03-09 19:55:191025 // Don't count unpacked extensions, since they're a developer-specific
1026 // feature.
[email protected]9b217652010-10-08 22:04:231027 if (location == Extension::LOAD)
[email protected]e8c729a2010-03-09 19:55:191028 continue;
1029
[email protected]9b217652010-10-08 22:04:231030 // Using an enumeration shows us the total installed ratio across all users.
1031 // Using the totals per user at each startup tells us the distribution of
1032 // usage for each user (e.g. 40% of users have at least one app installed).
1033 UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100);
1034 switch (type) {
1035 case Extension::TYPE_THEME:
1036 theme_count++;
1037 break;
1038 case Extension::TYPE_USER_SCRIPT:
1039 user_script_count++;
1040 break;
1041 case Extension::TYPE_HOSTED_APP:
1042 app_count++;
1043 hosted_app_count++;
1044 break;
1045 case Extension::TYPE_PACKAGED_APP:
1046 app_count++;
1047 packaged_app_count++;
1048 break;
1049 case Extension::TYPE_EXTENSION:
1050 default:
1051 extension_count++;
1052 break;
[email protected]cc2c3432009-11-06 17:24:361053 }
[email protected]9b217652010-10-08 22:04:231054 if (Extension::IsExternalLocation(location))
[email protected]1952c7d2010-03-04 23:48:341055 external_count++;
[email protected]9b217652010-10-08 22:04:231056 if ((*ex)->page_action() != NULL)
[email protected]1952c7d2010-03-04 23:48:341057 page_action_count++;
[email protected]9b217652010-10-08 22:04:231058 if ((*ex)->browser_action() != NULL)
[email protected]1952c7d2010-03-04 23:48:341059 browser_action_count++;
[email protected]cc2c3432009-11-06 17:24:361060 }
[email protected]9b217652010-10-08 22:04:231061 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp", app_count);
1062 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
1063 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp", packaged_app_count);
[email protected]1952c7d2010-03-04 23:48:341064 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
1065 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
1066 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
1067 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
1068 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
1069 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
1070 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:461071}
1072
[email protected]c6d474f82009-12-16 21:11:061073void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
1074 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:461075 std::string error;
1076 Extension* extension = NULL;
[email protected]306a2bd2010-08-11 14:56:361077 if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) {
1078 error = errors::kDisabledByPolicy;
1079 } else if (info.extension_manifest.get()) {
[email protected]c6d474f82009-12-16 21:11:061080 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:191081 bool require_key = info.extension_location != Extension::LOAD;
[email protected]92888082010-10-18 19:24:571082 tmp->set_location(info.extension_location);
[email protected]e8c729a2010-03-09 19:55:191083 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:461084 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:461085 } else {
[email protected]c6d474f82009-12-16 21:11:061086 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:461087 }
1088
1089 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:061090 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:401091 error,
1092 NotificationType::EXTENSION_INSTALL_ERROR,
1093 false);
[email protected]ae09ca62009-08-21 19:46:461094 return;
1095 }
1096
[email protected]c6d474f82009-12-16 21:11:061097 if (write_to_prefs)
1098 extension_prefs_->UpdateManifest(extension);
1099
[email protected]2a409532009-08-28 19:39:441100 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:461101
[email protected]55196e92010-09-29 15:04:461102 if (Extension::IsExternalLocation(info.extension_location)) {
[email protected]ca4b5fa32010-10-09 12:42:181103 BrowserThread::PostTask(
1104 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:061105 NewRunnableMethod(
[email protected]f17dbd42010-08-16 23:21:101106 backend_.get(),
1107 &ExtensionsServiceBackend::CheckExternalUninstall,
1108 scoped_refptr<ExtensionsService>(this),
1109 info.extension_id,
1110 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:461111 }
[email protected]9f1087e2009-06-15 17:29:321112}
1113
[email protected]62d30f42009-10-01 22:36:061114void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
[email protected]57a777f72010-03-31 01:09:421115 // The ChromeURLRequestContexts need to be first to know that the extension
[email protected]62d30f42009-10-01 22:36:061116 // was loaded, otherwise a race can arise where a renderer that is created
1117 // for the extension may try to load an extension URL with an extension id
[email protected]57a777f72010-03-31 01:09:421118 // that the request context doesn't yet know about. The profile is responsible
1119 // for ensuring its URLRequestContexts appropriately discover the loaded
1120 // extension.
1121 if (profile_) {
1122 profile_->RegisterExtensionWithRequestContexts(extension);
[email protected]24b538a2010-02-27 01:22:441123
1124 // Check if this permission requires unlimited storage quota
[email protected]c2c263c2010-08-13 21:59:481125 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
1126 GrantUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:421127
1128 // If the extension is an app, protect its local storage from
1129 // "Clear browsing data."
1130 if (extension->is_app())
1131 GrantProtectedStorage(extension);
[email protected]62d30f42009-10-01 22:36:061132 }
1133
[email protected]62d30f42009-10-01 22:36:061134 NotificationService::current()->Notify(
1135 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:341136 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:061137 Details<Extension>(extension));
1138}
1139
1140void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
[email protected]62d30f42009-10-01 22:36:061141 NotificationService::current()->Notify(
1142 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:341143 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:061144 Details<Extension>(extension));
1145
[email protected]57a777f72010-03-31 01:09:421146 if (profile_) {
1147 profile_->UnregisterExtensionWithRequestContexts(extension);
1148
1149 // Check if this permission required unlimited storage quota, reset its
1150 // in-memory quota.
[email protected]c2c263c2010-08-13 21:59:481151 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
1152 RevokeUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:421153
1154 // If this is an app, then stop protecting its storage so it can be deleted.
1155 if (extension->is_app())
1156 RevokeProtectedStorage(extension);
1157 }
1158}
1159
1160void ExtensionsService::GrantProtectedStorage(Extension* extension) {
1161 DCHECK(extension->is_app()) << "Only Apps are allowed protected storage.";
1162 std::vector<GURL> origins;
1163 GetExplicitOriginsInExtent(extension, &origins);
1164 for (size_t i = 0; i < origins.size(); ++i)
1165 ++protected_storage_map_[origins[i]];
1166}
1167
1168void ExtensionsService::RevokeProtectedStorage(Extension* extension) {
1169 DCHECK(extension->is_app()) << "Attempting to revoke protected storage from "
1170 << " a non-app extension.";
1171 std::vector<GURL> origins;
1172 GetExplicitOriginsInExtent(extension, &origins);
1173 for (size_t i = 0; i < origins.size(); ++i) {
1174 const GURL& origin = origins[i];
1175 DCHECK(protected_storage_map_[origin] > 0);
1176 if (--protected_storage_map_[origin] <= 0)
1177 protected_storage_map_.erase(origin);
[email protected]c2c263c2010-08-13 21:59:481178 }
1179}
1180
1181void ExtensionsService::GrantUnlimitedStorage(Extension* extension) {
1182 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
1183 std::vector<GURL> origins;
1184 GetExplicitOriginsInExtent(extension, &origins);
1185 origins.push_back(extension->url());
1186
1187 for (size_t i = 0; i < origins.size(); ++i) {
1188 const GURL& origin = origins[i];
1189 if (++unlimited_storage_map_[origin] == 1) {
[email protected]57a777f72010-03-31 01:09:421190 string16 origin_identifier =
[email protected]c2c263c2010-08-13 21:59:481191 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
[email protected]ca4b5fa32010-10-09 12:42:181192 BrowserThread::PostTask(
1193 BrowserThread::FILE, FROM_HERE,
[email protected]c2c263c2010-08-13 21:59:481194 NewRunnableMethod(
1195 profile_->GetDatabaseTracker(),
1196 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
1197 origin_identifier,
1198 kint64max));
[email protected]ca4b5fa32010-10-09 12:42:181199 BrowserThread::PostTask(
1200 BrowserThread::IO, FROM_HERE,
[email protected]c2c263c2010-08-13 21:59:481201 NewRunnableMethod(
1202 profile_->GetAppCacheService(),
1203 &ChromeAppCacheService::SetOriginQuotaInMemory,
1204 origin,
1205 kint64max));
[email protected]ca4b5fa32010-10-09 12:42:181206 BrowserThread::PostTask(
1207 BrowserThread::IO, FROM_HERE,
[email protected]70c6c042010-10-08 09:52:071208 NewRunnableMethod(
1209 profile_->GetFileSystemHostContext(),
1210 &FileSystemHostContext::SetOriginQuotaUnlimited,
1211 origin));
[email protected]c2c263c2010-08-13 21:59:481212 }
1213 }
1214}
1215
1216void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) {
1217 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
1218 std::vector<GURL> origins;
1219 GetExplicitOriginsInExtent(extension, &origins);
1220 origins.push_back(extension->url());
1221
1222 for (size_t i = 0; i < origins.size(); ++i) {
1223 const GURL& origin = origins[i];
1224 DCHECK(unlimited_storage_map_[origin] > 0);
1225 if (--unlimited_storage_map_[origin] == 0) {
1226 unlimited_storage_map_.erase(origin);
1227 string16 origin_identifier =
1228 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
[email protected]ca4b5fa32010-10-09 12:42:181229 BrowserThread::PostTask(
1230 BrowserThread::FILE, FROM_HERE,
[email protected]be180c802009-10-23 06:33:311231 NewRunnableMethod(
[email protected]57a777f72010-03-31 01:09:421232 profile_->GetDatabaseTracker(),
1233 &webkit_database::DatabaseTracker::ResetOriginQuotaInMemory,
1234 origin_identifier));
[email protected]ca4b5fa32010-10-09 12:42:181235 BrowserThread::PostTask(
1236 BrowserThread::IO, FROM_HERE,
[email protected]c2c263c2010-08-13 21:59:481237 NewRunnableMethod(
1238 profile_->GetAppCacheService(),
1239 &ChromeAppCacheService::ResetOriginQuotaInMemory,
1240 origin));
[email protected]ca4b5fa32010-10-09 12:42:181241 BrowserThread::PostTask(
1242 BrowserThread::IO, FROM_HERE,
[email protected]70c6c042010-10-08 09:52:071243 NewRunnableMethod(
1244 profile_->GetFileSystemHostContext(),
1245 &FileSystemHostContext::ResetOriginQuotaUnlimited,
1246 origin));
[email protected]62d30f42009-10-01 22:36:061247 }
1248 }
1249}
1250
[email protected]6b75ec32009-08-14 06:37:181251void ExtensionsService::UpdateExtensionBlacklist(
1252 const std::vector<std::string>& blacklist) {
1253 // Use this set to indicate if an extension in the blacklist has been used.
1254 std::set<std::string> blacklist_set;
1255 for (unsigned int i = 0; i < blacklist.size(); ++i) {
1256 if (Extension::IdIsValid(blacklist[i])) {
1257 blacklist_set.insert(blacklist[i]);
1258 }
1259 }
1260 extension_prefs_->UpdateBlacklist(blacklist_set);
1261 std::vector<std::string> to_be_removed;
1262 // Loop current extensions, unload installed extensions.
1263 for (ExtensionList::const_iterator iter = extensions_.begin();
1264 iter != extensions_.end(); ++iter) {
1265 Extension* extension = (*iter);
1266 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
1267 to_be_removed.push_back(extension->id());
1268 }
1269 }
1270
1271 // UnloadExtension will change the extensions_ list. So, we should
1272 // call it outside the iterator loop.
1273 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
1274 UnloadExtension(to_be_removed[i]);
1275 }
1276}
1277
[email protected]aa96d3a2010-08-21 08:45:251278void ExtensionsService::DestroyingProfile() {
[email protected]2fb7dc982010-09-29 12:24:281279 pref_change_registrar_.RemoveAll();
[email protected]aa96d3a2010-08-21 08:45:251280 profile_ = NULL;
[email protected]2fb7dc982010-09-29 12:24:281281 toolbar_model_.DestroyingProfile();
[email protected]aa96d3a2010-08-21 08:45:251282}
1283
1284void ExtensionsService::CheckAdminBlacklist() {
1285 std::vector<std::string> to_be_removed;
1286 // Loop through extensions list, unload installed extensions.
1287 for (ExtensionList::const_iterator iter = extensions_.begin();
1288 iter != extensions_.end(); ++iter) {
1289 Extension* extension = (*iter);
1290 if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id()))
1291 to_be_removed.push_back(extension->id());
1292 }
1293
1294 // UnloadExtension will change the extensions_ list. So, we should
1295 // call it outside the iterator loop.
1296 for (unsigned int i = 0; i < to_be_removed.size(); ++i)
1297 UnloadExtension(to_be_removed[i]);
1298}
1299
[email protected]cb0ce1e022010-03-10 19:54:411300bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
1301 // If this is a component extension we always allow it to work in incognito
1302 // mode.
1303 if (extension->location() == Extension::COMPONENT)
1304 return true;
1305
1306 // Check the prefs.
1307 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:501308}
[email protected]55a35692010-02-11 23:25:211309
[email protected]cb0ce1e022010-03-10 19:54:411310void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:501311 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:411312 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:241313
[email protected]568f33d2010-08-04 17:06:411314 // Broadcast unloaded and loaded events to update browser state. Only bother
1315 // if the extension is actually enabled, since there is no UI otherwise.
1316 bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
1317 extension) != extensions_.end();
1318 if (is_enabled) {
1319 NotifyExtensionUnloaded(extension);
1320 NotifyExtensionLoaded(extension);
1321 }
[email protected]55a35692010-02-11 23:25:211322}
1323
[email protected]05c82182010-06-24 17:49:081324bool ExtensionsService::AllowFileAccess(const Extension* extension) {
1325 return (CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]334e04a2010-06-24 23:34:441326 switches::kDisableExtensionsFileAccessCheck) ||
[email protected]05c82182010-06-24 17:49:081327 extension_prefs_->AllowFileAccess(extension->id()));
1328}
1329
1330void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
1331 extension_prefs_->SetAllowFileAccess(extension->id(), allow);
1332 NotificationService::current()->Notify(
1333 NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
1334 Source<Profile>(profile_),
1335 Details<Extension>(extension));
1336}
1337
[email protected]93fd78f42009-07-10 16:43:171338void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:321339 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:581340 // TODO(aa): Why pass this list into the provider, why not just filter it
1341 // later?
[email protected]9f1087e2009-06-15 17:29:321342 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:511343 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]ca4b5fa32010-10-09 12:42:181344 BrowserThread::PostTask(
1345 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:061346 NewRunnableMethod(
1347 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
1348 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:321349}
1350
1351void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:091352 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:391353 scoped_ptr<Extension> extension(
1354 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:251355
[email protected]e7afe2452010-08-22 16:19:131356 // Callers should not send us nonexistent extensions.
[email protected]0c6da502009-08-14 22:32:391357 CHECK(extension.get());
1358
[email protected]1eb175082010-02-10 09:26:161359 // Keep information about the extension so that we can reload it later
1360 // even if it's not permanently installed.
1361 unloaded_extension_paths_[extension->id()] = extension->path();
1362
[email protected]f17dbd42010-08-16 23:21:101363 // Clean up if the extension is meant to be enabled after a reload.
1364 disabled_extension_paths_.erase(extension->id());
1365
[email protected]86c008e82009-08-28 20:26:051366 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
1367 extension->GetChromeURLOverrides());
1368
[email protected]0c6da502009-08-14 22:32:391369 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
1370 disabled_extensions_.end(),
1371 extension.get());
1372 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:391373 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:471374 NotificationService::current()->Notify(
1375 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341376 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:471377 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:391378 return;
1379 }
1380
1381 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:571382
[email protected]631cf822009-05-15 07:01:251383 // Remove the extension from our list.
1384 extensions_.erase(iter);
1385
[email protected]62d30f42009-10-01 22:36:061386 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:351387 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:251388}
1389
[email protected]9f1087e2009-06-15 17:29:321390void ExtensionsService::UnloadAllExtensions() {
[email protected]cd500f72010-06-25 23:44:321391 STLDeleteContainerPointers(extensions_.begin(), extensions_.end());
[email protected]9f1087e2009-06-15 17:29:321392 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:291393
[email protected]cd500f72010-06-25 23:44:321394 STLDeleteContainerPointers(disabled_extensions_.begin(),
1395 disabled_extensions_.end());
1396 disabled_extensions_.clear();
1397
[email protected]c6e4a3412009-06-24 15:45:291398 // TODO(erikkay) should there be a notification for this? We can't use
1399 // EXTENSION_UNLOADED since that implies that the extension has been disabled
1400 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:321401}
1402
1403void ExtensionsService::ReloadExtensions() {
1404 UnloadAllExtensions();
1405 LoadAllExtensions();
1406}
1407
1408void ExtensionsService::GarbageCollectExtensions() {
[email protected]ba399672010-04-06 15:42:391409 if (extension_prefs_->pref_service()->read_only())
1410 return;
1411
[email protected]ca3dbf52010-05-19 22:27:061412 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
1413 extension_prefs_->GetInstalledExtensionsInfo());
1414
1415 std::map<std::string, FilePath> extension_paths;
1416 for (size_t i = 0; i < info->size(); ++i)
1417 extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
1418
[email protected]ca4b5fa32010-10-09 12:42:181419 BrowserThread::PostTask(
1420 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:061421 NewRunnableFunction(
1422 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]ca3dbf52010-05-19 22:27:061423 extension_paths));
[email protected]3cf4f0992009-02-03 23:00:301424}
1425
[email protected]e72e8eb82009-06-18 17:21:511426void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:131427 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:171428 if (updater_.get()) {
1429 updater_->Start();
1430 }
[email protected]e72e8eb82009-06-18 17:21:511431 NotificationService::current()->Notify(
1432 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:341433 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:511434 NotificationService::NoDetails());
1435}
1436
[email protected]6d2e60bd2010-06-03 22:37:391437void ExtensionsService::OnExtensionLoaded(Extension* extension,
[email protected]2a409532009-08-28 19:39:441438 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:461439 // Ensure extension is deleted unless we transfer ownership.
1440 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:321441
[email protected]1eb175082010-02-10 09:26:161442 // The extension is now loaded, remove its data from unloaded extension map.
1443 unloaded_extension_paths_.erase(extension->id());
1444
[email protected]f17dbd42010-08-16 23:21:101445 // If the extension was disabled for a reload, then enable it.
1446 if (disabled_extension_paths_.erase(extension->id()) > 0)
1447 EnableExtension(extension->id());
1448
[email protected]ceefd3d2010-03-12 09:10:291449 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
1450 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:461451 if (extensions_enabled() ||
[email protected]3ba0fd32010-06-19 05:39:101452 extension->is_theme() ||
[email protected]ae09ca62009-08-21 19:46:461453 extension->location() == Extension::LOAD ||
[email protected]f0b97f12010-10-11 21:44:351454 extension->location() == Extension::COMPONENT ||
[email protected]ae09ca62009-08-21 19:46:461455 Extension::IsExternalLocation(extension->location())) {
1456 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
1457 if (old) {
[email protected]ca3dbf52010-05-19 22:27:061458 // CrxInstaller should have guaranteed that we aren't downgrading.
1459 CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
[email protected]0c6da502009-08-14 22:32:391460
[email protected]ca3dbf52010-05-19 22:27:061461 bool allow_silent_upgrade =
1462 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
1463 old, extension);
[email protected]1e8c93f2010-02-08 22:58:311464
[email protected]ca3dbf52010-05-19 22:27:061465 // Extensions get upgraded if silent upgrades are allowed, otherwise
1466 // they get disabled.
1467 if (allow_silent_upgrade) {
1468 old->set_being_upgraded(true);
1469 extension->set_being_upgraded(true);
1470 }
[email protected]0c6da502009-08-14 22:32:391471
[email protected]ca3dbf52010-05-19 22:27:061472 // To upgrade an extension in place, unload the old one and
1473 // then load the new one.
1474 UnloadExtension(old->id());
1475 old = NULL;
1476
1477 if (!allow_silent_upgrade) {
1478 // Extension has changed permissions significantly. Disable it. We
1479 // send a notification below.
1480 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
1481 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]0c6da502009-08-14 22:32:391482 }
[email protected]ba74f352009-06-11 18:54:451483 }
[email protected]86a274072009-06-11 02:06:451484
[email protected]ae09ca62009-08-21 19:46:461485 switch (extension_prefs_->GetExtensionState(extension->id())) {
1486 case Extension::ENABLED:
1487 extensions_.push_back(scoped_extension.release());
1488
[email protected]62d30f42009-10-01 22:36:061489 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:461490
[email protected]e8c729a2010-03-09 19:55:191491 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
1492 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:461493 break;
1494 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:451495 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:401496 NotificationService::current()->Notify(
1497 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341498 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401499 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:461500 break;
1501 default:
[email protected]d11c8e92009-10-20 23:26:401502 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:461503 break;
[email protected]811f3432009-07-25 19:38:211504 }
[email protected]e72e8eb82009-06-18 17:21:511505 }
[email protected]aab98a52009-12-02 03:22:351506
[email protected]1e8c93f2010-02-08 22:58:311507 extension->set_being_upgraded(false);
1508
[email protected]aab98a52009-12-02 03:22:351509 UpdateActiveExtensionsInCrashReporter();
[email protected]0b004da2010-07-02 17:54:311510
1511 if (profile_->GetTemplateURLModel())
1512 profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension);
[email protected]b671760b2010-07-15 21:13:471513
1514 // Load the icon for omnibox-enabled extensions so it will be ready to display
1515 // in the URL bar.
[email protected]29d0d4ac2010-09-08 21:10:311516 if (!extension->omnibox_keyword().empty()) {
1517 omnibox_popup_icon_manager_.LoadIcon(extension);
[email protected]b671760b2010-07-15 21:13:471518 omnibox_icon_manager_.LoadIcon(extension);
[email protected]29d0d4ac2010-09-08 21:10:311519 }
[email protected]aab98a52009-12-02 03:22:351520}
1521
1522void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:391523 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:351524 for (size_t i = 0; i < extensions_.size(); ++i) {
[email protected]3ba0fd32010-06-19 05:39:101525 if (!extensions_[i]->is_theme())
[email protected]c8865962009-12-16 07:47:391526 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:351527 }
1528
1529 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:251530}
1531
[email protected]2a409532009-08-28 19:39:441532void ExtensionsService::OnExtensionInstalled(Extension* extension,
1533 bool allow_privilege_increase) {
[email protected]4416c5a2010-06-26 01:28:571534 // Ensure extension is deleted unless we transfer ownership.
1535 scoped_ptr<Extension> scoped_extension(extension);
1536 Extension::State initial_state = Extension::DISABLED;
1537 bool initial_enable_incognito = false;
[email protected]aa142702010-03-26 01:26:331538 PendingExtensionMap::iterator it =
1539 pending_extensions_.find(extension->id());
[email protected]4416c5a2010-06-26 01:28:571540 if (it != pending_extensions_.end()) {
[email protected]11edd1e2010-07-21 00:14:501541 PendingExtensionInfo pending_extension_info = it->second;
[email protected]8ef78fd2010-08-19 17:14:321542 PendingExtensionInfo::ExpectedCrxType expected_crx_type =
1543 pending_extension_info.expected_crx_type;
1544 bool is_from_sync = pending_extension_info.is_from_sync;
[email protected]11edd1e2010-07-21 00:14:501545 pending_extensions_.erase(it);
1546 it = pending_extensions_.end();
[email protected]8ef78fd2010-08-19 17:14:321547
[email protected]4416c5a2010-06-26 01:28:571548 // Set initial state from pending extension data.
[email protected]8ef78fd2010-08-19 17:14:321549 PendingExtensionInfo::ExpectedCrxType actual_crx_type =
[email protected]ec5b50d2010-10-09 16:35:181550 PendingExtensionInfo::EXTENSION;
1551 if (extension->is_app())
1552 actual_crx_type = PendingExtensionInfo::APP;
1553 else if (extension->is_theme())
1554 actual_crx_type = PendingExtensionInfo::THEME;
[email protected]8ef78fd2010-08-19 17:14:321555
1556 if (expected_crx_type != PendingExtensionInfo::UNKNOWN &&
1557 expected_crx_type != actual_crx_type) {
[email protected]4416c5a2010-06-26 01:28:571558 LOG(WARNING)
1559 << "Not installing pending extension " << extension->id()
[email protected]8ef78fd2010-08-19 17:14:321560 << " with is_theme = " << extension->is_theme();
[email protected]4416c5a2010-06-26 01:28:571561 // Delete the extension directory since we're not going to
1562 // load it.
[email protected]ca4b5fa32010-10-09 12:42:181563 BrowserThread::PostTask(
1564 BrowserThread::FILE, FROM_HERE,
[email protected]4416c5a2010-06-26 01:28:571565 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1566 return;
1567 }
[email protected]8ef78fd2010-08-19 17:14:321568
1569 // If |extension| is not syncable, and was installed via sync, disallow
1570 // the instanation.
1571 //
1572 // Themes are always allowed. Because they contain no active code, they
1573 // are less of a risk than extensions.
1574 //
1575 // If |is_from_sync| is false, then the install was not initiated by sync,
1576 // and this check should pass. Extensions that were installed from an
1577 // update URL in external_extensions.json are an example. They are not
1578 // syncable, because the user did not make an explicit choice to install
1579 // them. However, they were installed through the update mechanism, so
1580 // control must pass into this function.
1581 //
1582 // TODO(akalin): When we do apps sync, we have to work with its
1583 // traits, too.
[email protected]2a3e22b12010-08-13 04:55:171584 const browser_sync::ExtensionSyncTraits extension_sync_traits =
1585 browser_sync::GetExtensionSyncTraits();
[email protected]06e33202010-08-16 23:45:151586 const browser_sync::ExtensionSyncTraits app_sync_traits =
1587 browser_sync::GetAppSyncTraits();
[email protected]2a3e22b12010-08-13 04:55:171588 // If an extension is a theme, we bypass the valid/syncable check
1589 // as themes are harmless.
[email protected]8ef78fd2010-08-19 17:14:321590 if (!extension->is_theme() && is_from_sync &&
[email protected]2a3e22b12010-08-13 04:55:171591 !browser_sync::IsExtensionValidAndSyncable(
[email protected]06e33202010-08-16 23:45:151592 *extension, extension_sync_traits.allowed_extension_types) &&
1593 !browser_sync::IsExtensionValidAndSyncable(
1594 *extension, app_sync_traits.allowed_extension_types)) {
[email protected]11edd1e2010-07-21 00:14:501595 // We're an extension installed via sync that is unsyncable,
1596 // i.e. we may have been syncable previously. We block these
1597 // installs. We'll have to update the clause above if we decide
1598 // to sync other extension-like things, like apps or user
1599 // scripts.
1600 //
1601 // Note that this creates a small window where a user who tries
1602 // to download/install an extension that is simultaneously
1603 // installed via sync (and blocked) will find his download
1604 // blocked.
1605 //
1606 // TODO(akalin): Remove this check once we've put in UI to
1607 // approve synced extensions.
1608 LOG(WARNING)
[email protected]2a3e22b12010-08-13 04:55:171609 << "Not installing invalid or unsyncable extension "
1610 << extension->id();
[email protected]11edd1e2010-07-21 00:14:501611 // Delete the extension directory since we're not going to
1612 // load it.
[email protected]ca4b5fa32010-10-09 12:42:181613 BrowserThread::PostTask(
1614 BrowserThread::FILE, FROM_HERE,
[email protected]11edd1e2010-07-21 00:14:501615 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1616 return;
1617 }
[email protected]8ef78fd2010-08-19 17:14:321618 if (extension->is_theme()) {
[email protected]11edd1e2010-07-21 00:14:501619 DCHECK(pending_extension_info.enable_on_install);
[email protected]4416c5a2010-06-26 01:28:571620 initial_state = Extension::ENABLED;
[email protected]11edd1e2010-07-21 00:14:501621 DCHECK(!pending_extension_info.enable_incognito_on_install);
[email protected]4416c5a2010-06-26 01:28:571622 initial_enable_incognito = false;
1623 } else {
1624 initial_state =
[email protected]11edd1e2010-07-21 00:14:501625 pending_extension_info.enable_on_install ?
[email protected]4416c5a2010-06-26 01:28:571626 Extension::ENABLED : Extension::DISABLED;
1627 initial_enable_incognito =
[email protected]11edd1e2010-07-21 00:14:501628 pending_extension_info.enable_incognito_on_install;
[email protected]4416c5a2010-06-26 01:28:571629 }
[email protected]4416c5a2010-06-26 01:28:571630 } else {
[email protected]dbec3792010-08-10 00:08:451631 // Make sure we preserve enabled/disabled states.
[email protected]4416c5a2010-06-26 01:28:571632 Extension::State existing_state =
1633 extension_prefs_->GetExtensionState(extension->id());
1634 initial_state =
1635 (existing_state == Extension::DISABLED) ?
1636 Extension::DISABLED : Extension::ENABLED;
[email protected]dbec3792010-08-10 00:08:451637 initial_enable_incognito =
1638 extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]aa142702010-03-26 01:26:331639 }
1640
[email protected]9b217652010-10-08 22:04:231641 UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType",
1642 extension->GetHistogramType(), 100);
[email protected]4416c5a2010-06-26 01:28:571643 extension_prefs_->OnExtensionInstalled(
1644 extension, initial_state, initial_enable_incognito);
[email protected]25b34332009-06-05 21:53:191645
[email protected]92a5b1d2010-07-20 00:42:001646 // Unpacked extensions start off with file access since they are a developer
1647 // feature.
1648 if (extension->location() == Extension::LOAD)
1649 extension_prefs_->SetAllowFileAccess(extension->id(), true);
1650
[email protected]4a190632009-05-09 01:07:421651 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
1652 // to apply it.
[email protected]3ba0fd32010-06-19 05:39:101653 if (extension->is_theme()) {
[email protected]9ceb07342009-07-26 04:09:231654 NotificationService::current()->Notify(
1655 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341656 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:231657 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:271658 } else {
1659 NotificationService::current()->Notify(
1660 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341661 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:271662 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:421663 }
[email protected]7577a5c52009-07-30 06:21:581664
[email protected]ec5b50d2010-10-09 16:35:181665 if (extension->is_app()) {
1666 ExtensionIdSet installed_ids = GetAppIds();
1667 installed_ids.insert(extension->id());
1668 default_apps_.DidInstallApp(installed_ids);
1669 }
1670
[email protected]4416c5a2010-06-26 01:28:571671 // Transfer ownership of |extension| to OnExtensionLoaded.
1672 OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:421673}
1674
[email protected]0c6da502009-08-14 22:32:391675Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
1676 bool include_enabled,
1677 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:051678 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:391679 if (include_enabled) {
1680 for (ExtensionList::const_iterator iter = extensions_.begin();
1681 iter != extensions_.end(); ++iter) {
1682 if ((*iter)->id() == lowercase_id)
1683 return *iter;
1684 }
1685 }
1686 if (include_disabled) {
1687 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
1688 iter != disabled_extensions_.end(); ++iter) {
1689 if ((*iter)->id() == lowercase_id)
1690 return *iter;
1691 }
[email protected]ce5c4502009-05-06 16:46:111692 }
1693 return NULL;
1694}
1695
[email protected]d3071992010-10-08 15:24:071696Extension* ExtensionsService::GetWebStoreApp() {
1697 return GetExtensionById(extension_misc::kWebStoreAppId, false);
1698}
1699
[email protected]9f1087e2009-06-15 17:29:321700Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
[email protected]a888b29e62010-04-01 13:38:571701 return url.scheme() != chrome::kExtensionScheme ? NULL :
1702 GetExtensionById(url.host(), false);
1703}
1704
1705Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) {
1706 for (size_t i = 0; i < extensions_.size(); ++i) {
1707 if (extensions_[i]->web_extent().ContainsURL(url))
1708 return extensions_[i];
1709 }
1710 return NULL;
[email protected]9f1087e2009-06-15 17:29:321711}
1712
[email protected]583d45c12010-08-31 02:48:121713bool ExtensionsService::ExtensionBindingsAllowed(const GURL& url) {
1714 // Allow bindings for all packaged extension.
1715 if (GetExtensionByURL(url))
1716 return true;
1717
1718 // Allow bindings for all component, hosted apps.
1719 Extension* extension = GetExtensionByWebExtent(url);
1720 return (extension && extension->location() == Extension::COMPONENT);
1721}
1722
[email protected]6d2e60bd2010-06-03 22:37:391723Extension* ExtensionsService::GetExtensionByOverlappingWebExtent(
[email protected]9f72aa02010-06-25 10:01:051724 const ExtensionExtent& extent) {
[email protected]22c966c2010-06-26 06:35:021725 for (size_t i = 0; i < extensions_.size(); ++i) {
1726 if (extensions_[i]->web_extent().OverlapsWith(extent))
1727 return extensions_[i];
1728 }
1729
[email protected]6d2e60bd2010-06-03 22:37:391730 return NULL;
1731}
1732
[email protected]b671760b2010-07-15 21:13:471733const SkBitmap& ExtensionsService::GetOmniboxIcon(
1734 const std::string& extension_id) {
1735 return omnibox_icon_manager_.GetIcon(extension_id);
1736}
1737
[email protected]29d0d4ac2010-09-08 21:10:311738const SkBitmap& ExtensionsService::GetOmniboxPopupIcon(
1739 const std::string& extension_id) {
1740 return omnibox_popup_icon_manager_.GetIcon(extension_id);
1741}
1742
[email protected]a1257b12009-06-12 02:51:341743void ExtensionsService::ClearProvidersForTesting() {
[email protected]ca4b5fa32010-10-09 12:42:181744 BrowserThread::PostTask(
1745 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:061746 NewRunnableMethod(
1747 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:341748}
1749
1750void ExtensionsService::SetProviderForTesting(
1751 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]ca4b5fa32010-10-09 12:42:181752 BrowserThread::PostTask(
1753 BrowserThread::FILE, FROM_HERE,
[email protected]95d29192009-10-30 01:49:061754 NewRunnableMethod(
1755 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
1756 location, test_provider));
[email protected]a1257b12009-06-12 02:51:341757}
1758
[email protected]8ef78fd2010-08-19 17:14:321759void ExtensionsService::OnExternalExtensionFileFound(
1760 const std::string& id,
1761 const std::string& version,
1762 const FilePath& path,
1763 Extension::Location location) {
[email protected]7577a5c52009-07-30 06:21:581764 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:491765 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:581766 // installed on every startup.
[email protected]61b411612009-11-10 23:17:411767 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:111768 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:581769 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:111770 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:581771 case -1: // existing version is older, we should upgrade
1772 break;
1773 case 0: // existing version is same, do nothing
1774 return;
1775 case 1: // existing version is newer, uh-oh
1776 LOG(WARNING) << "Found external version of extension " << id
1777 << "that is older than current version. Current version "
1778 << "is: " << existing->VersionString() << ". New version "
1779 << "is: " << version << ". Keeping current version.";
1780 return;
1781 }
1782 }
1783
[email protected]6dfbbf82010-03-12 23:09:161784 scoped_refptr<CrxInstaller> installer(
1785 new CrxInstaller(install_directory_,
1786 this, // frontend
1787 NULL)); // no client (silent install)
1788 installer->set_install_source(location);
1789 installer->set_expected_id(id);
1790 installer->set_allow_privilege_increase(true);
1791 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581792}
1793
[email protected]d11c8e92009-10-20 23:26:401794void ExtensionsService::ReportExtensionLoadError(
1795 const FilePath& extension_path,
1796 const std::string &error,
1797 NotificationType type,
1798 bool be_noisy) {
1799 NotificationService* service = NotificationService::current();
1800 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341801 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401802 Details<const std::string>(&error));
1803
1804 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161805 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]18d4b6c2010-09-21 03:21:041806 std::string message = base::StringPrintf(
1807 "Could not load extension from '%s'. %s",
1808 path_str.c_str(), error.c_str());
[email protected]d11c8e92009-10-20 23:26:401809 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1810}
1811
[email protected]406027c02010-09-27 08:03:181812void ExtensionsService::DidCreateRenderViewForBackgroundPage(
1813 ExtensionHost* host) {
1814 OrphanedDevTools::iterator iter =
1815 orphaned_dev_tools_.find(host->extension()->id());
1816 if (iter == orphaned_dev_tools_.end())
1817 return;
1818
1819 DevToolsManager::GetInstance()->AttachClientHost(
1820 iter->second, host->render_view_host());
1821 orphaned_dev_tools_.erase(iter);
1822}
1823
[email protected]4814b512009-11-07 00:12:291824void ExtensionsService::Observe(NotificationType type,
1825 const NotificationSource& source,
1826 const NotificationDetails& details) {
1827 switch (type.value) {
[email protected]a4ed6282009-12-14 20:51:161828 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]bc535ee52010-08-31 18:40:321829 if (profile_ != Source<Profile>(source).ptr()->GetOriginalProfile())
1830 break;
[email protected]a4ed6282009-12-14 20:51:161831
[email protected]f128af42010-08-05 18:05:261832 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1833
1834 // TODO(rafaelw): Remove this check and ExtensionHost::recently_deleted().
1835 // This is only here to help track down crbug.com/49114.
1836 ExtensionHost::HostPointerList::iterator iter =
1837 ExtensionHost::recently_deleted()->begin();
1838 for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
1839 if (*iter == host) {
1840 CHECK(host->GetURL().spec().size() + 2 != 0);
1841 break;
1842 }
1843 }
1844 if (iter == ExtensionHost::recently_deleted()->end())
1845 CHECK(host->GetURL().spec().size() + 1 != 0);
1846
[email protected]31f77262009-12-02 20:48:531847 // Unload the entire extension. We want it to be in a consistent state:
1848 // either fully working or not loaded at all, but never half-crashed.
[email protected]bc535ee52010-08-31 18:40:321849 // We do it in a PostTask so that other handlers of this notification will
1850 // still have access to the Extension and ExtensionHost.
1851 MessageLoop::current()->PostTask(FROM_HERE,
1852 NewRunnableMethod(this, &ExtensionsService::UnloadExtension,
1853 host->extension()->id()));
[email protected]31f77262009-12-02 20:48:531854 break;
1855 }
1856
[email protected]aa96d3a2010-08-21 08:45:251857 case NotificationType::PREF_CHANGED: {
1858 std::string* pref_name = Details<std::string>(details).ptr();
1859 DCHECK(*pref_name == prefs::kExtensionInstallAllowList ||
1860 *pref_name == prefs::kExtensionInstallDenyList);
1861 CheckAdminBlacklist();
1862 break;
1863 }
1864
[email protected]4814b512009-11-07 00:12:291865 default:
1866 NOTREACHED() << "Unexpected notification type.";
1867 }
1868}
1869
[email protected]ec5b50d2010-10-09 16:35:181870bool ExtensionsService::HasApps() const {
1871 return !GetAppIds().empty();
1872}
[email protected]377011d2010-07-20 04:18:501873
[email protected]ec5b50d2010-10-09 16:35:181874ExtensionIdSet ExtensionsService::GetAppIds() const {
1875 ExtensionIdSet result;
[email protected]377011d2010-07-20 04:18:501876 for (ExtensionList::const_iterator it = extensions_.begin();
1877 it != extensions_.end(); ++it) {
[email protected]ec5b50d2010-10-09 16:35:181878 if ((*it)->is_app() && (*it)->location() != Extension::COMPONENT)
1879 result.insert((*it)->id());
[email protected]377011d2010-07-20 04:18:501880 }
1881
[email protected]ec5b50d2010-10-09 16:35:181882 return result;
[email protected]377011d2010-07-20 04:18:501883}