blob: 03699fa81c49f21cd4d52849a1a9eef4f1c1701c [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]cc2c3432009-11-06 17:24:3612#include "base/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]dbb92e0d2009-08-20 16:18:2123#include "chrome/browser/chrome_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]5cbe1e22010-01-30 01:18:5626#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3827#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2828#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]2c5e1e12010-06-10 13:14:4429#include "chrome/browser/extensions/extension_cookies_api.h"
[email protected]c10da4b02010-03-25 14:38:3230#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0531#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]14a000d2010-04-29 21:44:2432#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]de768a832009-10-30 05:25:0133#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5634#include "chrome/browser/extensions/extension_host.h"
[email protected]7596ce72010-08-30 05:10:4635#include "chrome/browser/extensions/extension_management_api.h"
[email protected]4814b512009-11-07 00:12:2936#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1737#include "chrome/browser/extensions/extension_updater.h"
[email protected]784688a62010-09-13 07:06:5238#include "chrome/browser/extensions/extension_webnavigation_api.h"
[email protected]a1257b12009-06-12 02:51:3439#include "chrome/browser/extensions/external_extension_provider.h"
40#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]56ad3792010-05-28 17:45:3341#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]37858e52010-08-26 00:22:0242#include "chrome/browser/prefs/pref_service.h"
[email protected]81e63782009-02-27 19:35:0943#include "chrome/browser/profile.h"
[email protected]56ad3792010-05-28 17:45:3344#include "chrome/browser/search_engines/template_url_model.h"
[email protected]2a3e22b12010-08-13 04:55:1745#include "chrome/browser/sync/glue/extension_sync_traits.h"
[email protected]11edd1e2010-07-21 00:14:5046#include "chrome/browser/sync/glue/extension_util.h"
[email protected]aab98a52009-12-02 03:22:3547#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5448#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5849#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4050#include "chrome/common/extensions/extension_constants.h"
[email protected]05c82182010-06-24 17:49:0851#include "chrome/common/extensions/extension_error_utils.h"
[email protected]7c927b62010-02-24 09:54:1352#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0653#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2654#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2955#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3456#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1957#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1458#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3259#include "googleurl/src/gurl.h"
[email protected]24b538a2010-02-27 01:22:4460#include "webkit/database/database_tracker.h"
61#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3162
[email protected]79db6232009-02-13 20:51:2063#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3464#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2065#endif
[email protected]6014d672008-12-05 00:38:2566
[email protected]5ef47ec2010-01-28 05:58:0567using base::Time;
68
[email protected]c6d474f82009-12-16 21:11:0669namespace errors = extension_manifest_errors;
70
[email protected]b6ab96d2009-08-20 18:58:1971namespace {
72
[email protected]29d0d4ac2010-09-08 21:10:3173#if defined(OS_LINUX)
74static const int kOmniboxIconPaddingLeft = 2;
75static const int kOmniboxIconPaddingRight = 2;
76#elif defined(OS_MACOSX)
77static const int kOmniboxIconPaddingLeft = 0;
78static const int kOmniboxIconPaddingRight = 2;
79#else
80static const int kOmniboxIconPaddingLeft = 0;
81static const int kOmniboxIconPaddingRight = 0;
82#endif
83
[email protected]c2c263c2010-08-13 21:59:4884bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
[email protected]2111b1a2010-03-12 18:12:4485 // Always reload LOAD extension manifests, because they can change on disk
86 // independent of the manifest in our prefs.
87 if (info.extension_location == Extension::LOAD)
88 return true;
89
90 // Otherwise, reload the manifest it needs to be relocalized.
91 return extension_l10n_util::ShouldRelocalizeManifest(info);
92}
93
[email protected]c2c263c2010-08-13 21:59:4894void GetExplicitOriginsInExtent(Extension* extension,
95 std::vector<GURL>* origins) {
96 typedef std::vector<URLPattern> PatternList;
97 std::set<GURL> set;
98 const PatternList& patterns = extension->web_extent().patterns();
99 for (PatternList::const_iterator pattern = patterns.begin();
100 pattern != patterns.end(); ++pattern) {
101 if (pattern->match_subdomains() || pattern->match_all_urls())
102 continue;
[email protected]654512b2010-09-01 02:09:42103 // Wildcard URL schemes won't parse into a valid GURL, so explicit schemes
104 // must be used.
105 PatternList explicit_patterns = pattern->ConvertToExplicitSchemes();
106 for (PatternList::const_iterator explicit_p = explicit_patterns.begin();
107 explicit_p != explicit_patterns.end(); ++explicit_p) {
108 GURL origin = GURL(explicit_p->GetAsString()).GetOrigin();
109 if (origin.is_valid()) {
110 set.insert(origin);
111 } else {
112 NOTREACHED();
113 }
114 }
[email protected]c2c263c2010-08-13 21:59:48115 }
116
117 for (std::set<GURL>::const_iterator unique = set.begin();
118 unique != set.end(); ++unique) {
119 origins->push_back(*unique);
120 }
121}
122
[email protected]c6d474f82009-12-16 21:11:06123} // namespace
[email protected]b6ab96d2009-08-20 18:58:19124
[email protected]8ef78fd2010-08-19 17:14:32125PendingExtensionInfo::PendingExtensionInfo(
126 const GURL& update_url,
127 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
128 bool is_from_sync,
129 bool install_silently,
130 bool enable_on_install,
131 bool enable_incognito_on_install)
[email protected]aa142702010-03-26 01:26:33132 : update_url(update_url),
[email protected]8ef78fd2010-08-19 17:14:32133 expected_crx_type(expected_crx_type),
134 is_from_sync(is_from_sync),
[email protected]4416c5a2010-06-26 01:28:57135 install_silently(install_silently),
136 enable_on_install(enable_on_install),
137 enable_incognito_on_install(enable_incognito_on_install) {}
[email protected]aa142702010-03-26 01:26:33138
139PendingExtensionInfo::PendingExtensionInfo()
140 : update_url(),
[email protected]8ef78fd2010-08-19 17:14:32141 expected_crx_type(PendingExtensionInfo::UNKNOWN),
142 is_from_sync(true),
[email protected]4416c5a2010-06-26 01:28:57143 install_silently(false),
144 enable_on_install(false),
145 enable_incognito_on_install(false) {}
[email protected]aa142702010-03-26 01:26:33146
[email protected]25b34332009-06-05 21:53:19147// ExtensionsService.
[email protected]6014d672008-12-05 00:38:25148
[email protected]cc655912009-01-29 23:19:19149const char* ExtensionsService::kInstallDirectoryName = "Extensions";
150const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42151
[email protected]06f37542010-08-14 18:26:28152namespace {
153
154bool IsGalleryDownloadURL(const GURL& download_url) {
[email protected]b7c2f252009-12-08 00:47:23155 if (StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44156 extension_urls::kMiniGalleryDownloadPrefix, false))
157 return true;
[email protected]b7c2f252009-12-08 00:47:23158
[email protected]06f37542010-08-14 18:26:28159 if (StartsWithASCII(download_url.spec(),
160 extension_urls::kGalleryDownloadPrefix, false))
161 return true;
[email protected]b7c2f252009-12-08 00:47:23162
[email protected]473ff6e2010-05-12 15:31:55163 // Allow command line gallery url to be referrer for the gallery downloads.
164 std::string command_line_gallery_url =
165 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
[email protected]2bce5e12010-05-13 01:18:07166 switches::kAppsGalleryURL);
[email protected]473ff6e2010-05-12 15:31:55167 if (!command_line_gallery_url.empty() &&
168 StartsWithASCII(download_url.spec(),
[email protected]dd9d6272010-09-09 17:33:18169 command_line_gallery_url, false))
[email protected]473ff6e2010-05-12 15:31:55170 return true;
[email protected]334e04a2010-06-24 23:34:44171
172 return false;
173}
174
[email protected]06f37542010-08-14 18:26:28175} // namespace
176
[email protected]7a4c6852010-09-16 03:44:22177// Implements IO for the ExtensionsService.
178
179class ExtensionsServiceBackend
180 : public base::RefCountedThreadSafe<ExtensionsServiceBackend>,
181 public ExternalExtensionProvider::Visitor {
182 public:
[email protected]1f830eb2010-09-28 08:25:14183 // |install_directory| is a path where to look for extensions to load.
184 // |load_external_extensions| indicates whether or not backend should load
185 // external extensions listed in JSON file and Windows registry.
186 ExtensionsServiceBackend(const FilePath& install_directory,
187 bool load_external_extensions);
[email protected]7a4c6852010-09-16 03:44:22188
189 // Loads a single extension from |path| where |path| is the top directory of
190 // a specific extension where its manifest file lives.
191 // Errors are reported through ExtensionErrorReporter. On success,
192 // OnExtensionLoaded() is called.
193 // TODO(erikkay): It might be useful to be able to load a packed extension
194 // (presumably into memory) without installing it.
195 void LoadSingleExtension(const FilePath &path,
196 scoped_refptr<ExtensionsService> frontend);
197
198 // Check externally updated extensions for updates and install if necessary.
199 // Errors are reported through ExtensionErrorReporter. Succcess is not
200 // reported.
201 void CheckForExternalUpdates(std::set<std::string> ids_to_ignore,
202 scoped_refptr<ExtensionsService> frontend);
203
204 // For the extension in |version_path| with |id|, check to see if it's an
205 // externally managed extension. If so, tell the frontend to uninstall it.
206 void CheckExternalUninstall(scoped_refptr<ExtensionsService> frontend,
207 const std::string& id,
208 Extension::Location location);
209
210 // Clear all ExternalExtensionProviders.
211 void ClearProvidersForTesting();
212
213 // Sets an ExternalExtensionProvider for the service to use during testing.
214 // |location| specifies what type of provider should be added.
215 void SetProviderForTesting(Extension::Location location,
216 ExternalExtensionProvider* test_provider);
217
218 // ExternalExtensionProvider::Visitor implementation.
219 virtual void OnExternalExtensionFileFound(const std::string& id,
220 const Version* version,
221 const FilePath& path,
222 Extension::Location location);
223
[email protected]a424d84c2010-09-24 09:31:15224 virtual void OnExternalExtensionUpdateUrlFound(const std::string& id,
225 const GURL& update_url);
[email protected]7a4c6852010-09-16 03:44:22226
227 // Reloads the given extensions from their manifests on disk (instead of what
228 // we have cached in the prefs).
229 void ReloadExtensionManifests(
230 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
231 base::TimeTicks start_time,
232 scoped_refptr<ExtensionsService> frontend);
233
234 private:
235 friend class base::RefCountedThreadSafe<ExtensionsServiceBackend>;
236
237 virtual ~ExtensionsServiceBackend();
238
239 // Finish installing the extension in |crx_path| after it has been unpacked to
240 // |unpacked_path|. If |expected_id| is not empty, it's verified against the
241 // extension's manifest before installation. If |silent| is true, there will
242 // be no install confirmation dialog. |from_gallery| indicates whether the
243 // crx was installed from our gallery, which results in different UI.
244 //
245 // Note: We take ownership of |extension|.
246 void OnExtensionUnpacked(const FilePath& crx_path,
247 const FilePath& unpacked_path,
248 Extension* extension,
249 const std::string expected_id);
250
251 // Notify the frontend that there was an error loading an extension.
252 void ReportExtensionLoadError(const FilePath& extension_path,
253 const std::string& error);
254
255 // Lookup an external extension by |id| by going through all registered
256 // external extension providers until we find a provider that contains an
257 // extension that matches. If |version| is not NULL, the extension version
258 // will be returned (caller is responsible for deleting that pointer).
259 // |location| can also be null, if not needed. Returns true if extension is
260 // found, false otherwise.
261 bool LookupExternalExtension(const std::string& id,
262 Version** version,
263 Extension::Location* location);
264
265 // This is a naked pointer which is set by each entry point.
266 // The entry point is responsible for ensuring lifetime.
267 ExtensionsService* frontend_;
268
269 // The top-level extensions directory being installed to.
270 FilePath install_directory_;
271
272 // Whether errors result in noisy alerts.
273 bool alert_on_error_;
274
[email protected]55196e92010-09-29 15:04:46275 // A map from external extension type to the external extension provider
276 // for that type. Because a single provider may handle more than one
277 // external extension type, more than one key may map to the same object.
[email protected]7a4c6852010-09-16 03:44:22278 typedef std::map<Extension::Location,
279 linked_ptr<ExternalExtensionProvider> > ProviderMap;
280 ProviderMap external_extension_providers_;
281
282 // Set to true by OnExternalExtensionUpdateUrlFound() when an external
283 // extension URL is found. Used in CheckForExternalUpdates() to see
284 // if an update check is needed to install pending extensions.
285 bool external_extension_added_;
286
287 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceBackend);
288};
289
290ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]1f830eb2010-09-28 08:25:14291 const FilePath& install_directory,
292 bool load_external_extensions)
[email protected]7a4c6852010-09-16 03:44:22293 : frontend_(NULL),
294 install_directory_(install_directory),
295 alert_on_error_(false),
296 external_extension_added_(false) {
[email protected]1f830eb2010-09-28 08:25:14297 if (!load_external_extensions)
298 return;
299
[email protected]7a4c6852010-09-16 03:44:22300 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
301 // pref data in the ctor and that is called on the UI thread. Would be better
302 // to re-read data each time we list external extensions, anyway.
303 external_extension_providers_[Extension::EXTERNAL_PREF] =
304 linked_ptr<ExternalExtensionProvider>(
305 new ExternalPrefExtensionProvider());
[email protected]55196e92010-09-29 15:04:46306 // EXTERNAL_PREF_DOWNLOAD and EXTERNAL_PREF extensions are handled by the
307 // same object.
308 external_extension_providers_[Extension::EXTERNAL_PREF_DOWNLOAD] =
309 external_extension_providers_[Extension::EXTERNAL_PREF];
[email protected]7a4c6852010-09-16 03:44:22310#if defined(OS_WIN)
311 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
312 linked_ptr<ExternalExtensionProvider>(
313 new ExternalRegistryExtensionProvider());
314#endif
315}
316
317ExtensionsServiceBackend::~ExtensionsServiceBackend() {
318}
319
320void ExtensionsServiceBackend::LoadSingleExtension(
321 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
322 frontend_ = frontend;
323
324 // Explicit UI loads are always noisy.
325 alert_on_error_ = true;
326
327 FilePath extension_path = path_in;
328 file_util::AbsolutePath(&extension_path);
329
[email protected]7a4c6852010-09-16 03:44:22330 std::string error;
331 Extension* extension = extension_file_util::LoadExtension(
332 extension_path,
333 false, // Don't require id
334 &error);
335
336 if (!extension) {
337 ReportExtensionLoadError(extension_path, error);
338 return;
339 }
340
341 extension->set_location(Extension::LOAD);
342
343 // Report this as an installed extension so that it gets remembered in the
344 // prefs.
345 ChromeThread::PostTask(
346 ChromeThread::UI, FROM_HERE,
347 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
348 extension, true));
349}
350
351void ExtensionsServiceBackend::ReportExtensionLoadError(
352 const FilePath& extension_path, const std::string &error) {
353 ChromeThread::PostTask(
354 ChromeThread::UI, FROM_HERE,
355 NewRunnableMethod(
356 frontend_,
357 &ExtensionsService::ReportExtensionLoadError, extension_path,
358 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
359}
360
361bool ExtensionsServiceBackend::LookupExternalExtension(
362 const std::string& id, Version** version, Extension::Location* location) {
363 scoped_ptr<Version> extension_version;
364 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
365 i != external_extension_providers_.end(); ++i) {
366 const ExternalExtensionProvider* provider = i->second.get();
367 extension_version.reset(provider->RegisteredVersion(id, location));
368 if (extension_version.get()) {
369 if (version)
370 *version = extension_version.release();
371 return true;
372 }
373 }
374 return false;
375}
376
377// Some extensions will autoupdate themselves externally from Chrome. These
378// are typically part of some larger client application package. To support
379// these, the extension will register its location in the the preferences file
380// (and also, on Windows, in the registry) and this code will periodically
381// check that location for a .crx file, which it will then install locally if
382// a new version is available.
383void ExtensionsServiceBackend::CheckForExternalUpdates(
384 std::set<std::string> ids_to_ignore,
385 scoped_refptr<ExtensionsService> frontend) {
386 // Note that this installation is intentionally silent (since it didn't
387 // go through the front-end). Extensions that are registered in this
388 // way are effectively considered 'pre-bundled', and so implicitly
389 // trusted. In general, if something has HKLM or filesystem access,
390 // they could install an extension manually themselves anyway.
391 alert_on_error_ = false;
392 frontend_ = frontend;
393 external_extension_added_ = false;
394
395 // Ask each external extension provider to give us a call back for each
396 // extension they know about. See OnExternalExtension(File|UpdateUrl)Found.
397
398 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
399 i != external_extension_providers_.end(); ++i) {
400 ExternalExtensionProvider* provider = i->second.get();
401 provider->VisitRegisteredExtension(this, ids_to_ignore);
402 }
403
404 if (external_extension_added_ && frontend->updater()) {
405 ChromeThread::PostTask(
406 ChromeThread::UI, FROM_HERE,
407 NewRunnableMethod(
408 frontend->updater(), &ExtensionUpdater::CheckNow));
409 }
410}
411
412void ExtensionsServiceBackend::CheckExternalUninstall(
413 scoped_refptr<ExtensionsService> frontend, const std::string& id,
414 Extension::Location location) {
415 // Check if the providers know about this extension.
416 ProviderMap::const_iterator i = external_extension_providers_.find(location);
417 if (i == external_extension_providers_.end()) {
418 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
419 << location;
420 return;
421 }
422
423 scoped_ptr<Version> version;
424 version.reset(i->second->RegisteredVersion(id, NULL));
425 if (version.get())
426 return; // Yup, known extension, don't uninstall.
427
428 // This is an external extension that we don't have registered. Uninstall.
429 ChromeThread::PostTask(
430 ChromeThread::UI, FROM_HERE,
431 NewRunnableMethod(
432 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
433}
434
435void ExtensionsServiceBackend::ClearProvidersForTesting() {
436 external_extension_providers_.clear();
437}
438
439void ExtensionsServiceBackend::SetProviderForTesting(
440 Extension::Location location,
441 ExternalExtensionProvider* test_provider) {
442 DCHECK(test_provider);
443 external_extension_providers_[location] =
444 linked_ptr<ExternalExtensionProvider>(test_provider);
445}
446
447void ExtensionsServiceBackend::OnExternalExtensionFileFound(
448 const std::string& id, const Version* version, const FilePath& path,
449 Extension::Location location) {
450 DCHECK(version);
451 ChromeThread::PostTask(
452 ChromeThread::UI, FROM_HERE,
453 NewRunnableMethod(
454 frontend_, &ExtensionsService::OnExternalExtensionFileFound, id,
455 version->GetString(), path, location));
456}
457
458void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound(
459 const std::string& id,
[email protected]a424d84c2010-09-24 09:31:15460 const GURL& update_url) {
[email protected]7a4c6852010-09-16 03:44:22461 if (frontend_->GetExtensionById(id, true)) {
462 // Already installed. Do not change the update URL that the extension set.
463 return;
464 }
465
[email protected]a424d84c2010-09-24 09:31:15466 frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url);
[email protected]7a4c6852010-09-16 03:44:22467 external_extension_added_ |= true;
468}
469
470void ExtensionsServiceBackend::ReloadExtensionManifests(
471 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
472 base::TimeTicks start_time,
473 scoped_refptr<ExtensionsService> frontend) {
474 frontend_ = frontend;
475
476 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
477 ExtensionInfo* info = extensions_to_reload->at(i).get();
478 if (!ShouldReloadExtensionManifest(*info))
479 continue;
480
481 // We need to reload original manifest in order to localize properly.
482 std::string error;
483 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
484 info->extension_path, false, &error));
485
486 if (extension.get())
487 extensions_to_reload->at(i)->extension_manifest.reset(
488 static_cast<DictionaryValue*>(
489 extension->manifest_value()->DeepCopy()));
490 }
491
492 // Finish installing on UI thread.
493 ChromeThread::PostTask(
494 ChromeThread::UI, FROM_HERE,
495 NewRunnableMethod(
496 frontend_,
497 &ExtensionsService::ContinueLoadAllExtensions,
498 extensions_to_reload,
499 start_time,
500 true));
501}
502
[email protected]334e04a2010-06-24 23:34:44503// static
504bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
505 const GURL& referrer_url) {
506 if (!IsGalleryDownloadURL(download_url))
507 return false;
508
509 if (StartsWithASCII(referrer_url.spec(),
510 extension_urls::kMiniGalleryBrowsePrefix, false))
511 return true;
512
513 if (StartsWithASCII(referrer_url.spec(),
514 Extension::ChromeStoreURL(), false))
515 return true;
516
517 // Allow command line gallery url to be referrer for the gallery downloads.
518 std::string command_line_gallery_url =
519 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
520 switches::kAppsGalleryURL);
521 if (!command_line_gallery_url.empty() &&
522 StartsWithASCII(referrer_url.spec(),
523 command_line_gallery_url, false))
524 return true;
[email protected]473ff6e2010-05-12 15:31:55525
[email protected]b7c2f252009-12-08 00:47:23526 return false;
527}
528
[email protected]ac025282009-12-16 19:16:38529bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
530 return StartsWithASCII(download_url.spec(),
531 extension_urls::kMiniGalleryDownloadPrefix,
532 false); // case_sensitive
533}
534
[email protected]81e63782009-02-27 19:35:09535ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08536 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23537 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17538 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12539 : profile_(profile),
[email protected]2fb7dc982010-09-29 12:24:28540 extension_prefs_(new ExtensionPrefs(profile->GetPrefs(),
541 install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23542 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23543 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13544 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31545 ready_(false),
546 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08547 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23548 if (command_line->HasSwitch(switches::kDisableExtensions)) {
549 extensions_enabled_ = false;
550 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
551 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18552 }
[email protected]36a784c2009-06-23 06:21:08553
[email protected]a4ed6282009-12-14 20:51:16554 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]bc535ee52010-08-31 18:40:32555 NotificationService::AllSources());
[email protected]2fb7dc982010-09-29 12:24:28556 pref_change_registrar_.Init(profile->GetPrefs());
557 pref_change_registrar_.Add(prefs::kExtensionInstallAllowList, this);
558 pref_change_registrar_.Add(prefs::kExtensionInstallDenyList, this);
[email protected]4814b512009-11-07 00:12:29559
[email protected]93fd78f42009-07-10 16:43:17560 // Set up the ExtensionUpdater
561 if (autoupdate_enabled) {
562 int update_frequency = kDefaultUpdateFrequencySeconds;
563 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]e83326f2010-07-31 17:29:25564 base::StringToInt(command_line->GetSwitchValueASCII(
565 switches::kExtensionsUpdateFrequency),
566 &update_frequency);
[email protected]93fd78f42009-07-10 16:43:17567 }
[email protected]2fb7dc982010-09-29 12:24:28568 updater_ = new ExtensionUpdater(this,
569 profile->GetPrefs(),
570 update_frequency);
[email protected]93fd78f42009-07-10 16:43:17571 }
572
[email protected]1f830eb2010-09-28 08:25:14573 backend_ = new ExtensionsServiceBackend(install_directory_,
574 extensions_enabled_);
[email protected]b671760b2010-07-15 21:13:47575
[email protected]aa96d3a2010-08-21 08:45:25576 // Use monochrome icons for Omnibox icons.
[email protected]29d0d4ac2010-09-08 21:10:31577 omnibox_popup_icon_manager_.set_monochrome(true);
[email protected]b671760b2010-07-15 21:13:47578 omnibox_icon_manager_.set_monochrome(true);
[email protected]29d0d4ac2010-09-08 21:10:31579 omnibox_icon_manager_.set_padding(gfx::Insets(0, kOmniboxIconPaddingLeft,
580 0, kOmniboxIconPaddingRight));
[email protected]6014d672008-12-05 00:38:25581}
582
583ExtensionsService::~ExtensionsService() {
[email protected]2fb7dc982010-09-29 12:24:28584 DCHECK(!profile_); // Profile should have told us it's going away.
[email protected]9f1087e2009-06-15 17:29:32585 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17586 if (updater_.get()) {
587 updater_->Stop();
588 }
[email protected]6014d672008-12-05 00:38:25589}
590
[email protected]c5ae74ab2010-04-15 18:14:37591void ExtensionsService::InitEventRouters() {
592 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
593 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]56ee0152010-06-16 01:54:42594 ExtensionBrowserEventRouter::GetInstance()->Init(profile_);
[email protected]c5ae74ab2010-04-15 18:14:37595 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
596 profile_->GetBookmarkModel());
[email protected]2c5e1e12010-06-10 13:14:44597 ExtensionCookiesEventRouter::GetInstance()->Init();
[email protected]7596ce72010-08-30 05:10:46598 ExtensionManagementEventRouter::GetInstance()->Init();
[email protected]784688a62010-09-13 07:06:52599 ExtensionWebNavigationEventRouter::GetInstance()->Init();
[email protected]c5ae74ab2010-04-15 18:14:37600}
601
[email protected]9f1087e2009-06-15 17:29:32602void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29603 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17604 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32605
[email protected]95dd38f2009-10-20 20:09:15606 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
607 // the first extension, because its members listen for loaded notifications.
608 g_browser_process->resource_dispatcher_host();
609
[email protected]9f1087e2009-06-15 17:29:32610 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57611
[email protected]9f1087e2009-06-15 17:29:32612 // TODO(erikkay) this should probably be deferred to a future point
613 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17614 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57615
[email protected]9f1087e2009-06-15 17:29:32616 // TODO(erikkay) this should probably be deferred as well.
617 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25618}
619
[email protected]3cf4f0992009-02-03 23:00:30620void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16621 scoped_refptr<CrxInstaller> installer(
622 new CrxInstaller(install_directory_,
623 this, // frontend
624 NULL)); // no client (silent install)
625 installer->set_allow_privilege_increase(true);
626 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30627}
628
[email protected]aa142702010-03-26 01:26:33629namespace {
630 // TODO(akalin): Put this somewhere where both crx_installer.cc and
631 // this file can use it.
632 void DeleteFileHelper(const FilePath& path, bool recursive) {
633 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
634 file_util::Delete(path, recursive);
635 }
636} // namespace
637
[email protected]e957fe52009-06-23 16:51:05638void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34639 const FilePath& extension_path,
640 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33641 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
[email protected]8ef78fd2010-08-19 17:14:32642 bool is_pending_extension = (it != pending_extensions_.end());
643
644 if (!is_pending_extension &&
[email protected]aa142702010-03-26 01:26:33645 !GetExtensionByIdInternal(id, true, true)) {
646 LOG(WARNING) << "Will not update extension " << id
647 << " because it is not installed or pending";
648 // Delete extension_path since we're not creating a CrxInstaller
649 // that would do it for us.
650 ChromeThread::PostTask(
651 ChromeThread::FILE, FROM_HERE,
652 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49653 return;
[email protected]e957fe52009-06-23 16:51:05654 }
655
[email protected]aa142702010-03-26 01:26:33656 // We want a silent install only for non-pending extensions and
657 // pending extensions that have install_silently set.
658 ExtensionInstallUI* client =
[email protected]8ef78fd2010-08-19 17:14:32659 (!is_pending_extension || it->second.install_silently) ?
[email protected]aa142702010-03-26 01:26:33660 NULL : new ExtensionInstallUI(profile_);
661
[email protected]6dfbbf82010-03-12 23:09:16662 scoped_refptr<CrxInstaller> installer(
663 new CrxInstaller(install_directory_,
664 this, // frontend
[email protected]aa142702010-03-26 01:26:33665 client));
[email protected]6dfbbf82010-03-12 23:09:16666 installer->set_expected_id(id);
[email protected]8ef78fd2010-08-19 17:14:32667 if (is_pending_extension && !it->second.is_from_sync)
668 installer->set_install_source(Extension::EXTERNAL_PREF_DOWNLOAD);
[email protected]6dfbbf82010-03-12 23:09:16669 installer->set_delete_source(true);
[email protected]5c8516202010-03-18 21:43:34670 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16671 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05672}
673
[email protected]8ef78fd2010-08-19 17:14:32674void ExtensionsService::AddPendingExtensionFromSync(
[email protected]aa142702010-03-26 01:26:33675 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32676 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
677 bool install_silently, bool enable_on_install,
678 bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33679 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25680 LOG(DFATAL) << "Trying to add pending extension " << id
681 << " which already exists";
[email protected]aa142702010-03-26 01:26:33682 return;
683 }
[email protected]4416c5a2010-06-26 01:28:57684 AddPendingExtensionInternal(
[email protected]8ef78fd2010-08-19 17:14:32685 id, update_url, expected_crx_type, true, install_silently,
[email protected]4416c5a2010-06-26 01:28:57686 enable_on_install, enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33687}
688
[email protected]8ef78fd2010-08-19 17:14:32689void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl(
[email protected]a424d84c2010-09-24 09:31:15690 const std::string& id, const GURL& update_url) {
[email protected]8ef78fd2010-08-19 17:14:32691 // Add the extension to this list of extensions to update.
692 // We do not know if the id refers to a theme, so make is_theme unknown.
693 const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType =
694 PendingExtensionInfo::UNKNOWN;
695 const bool kIsFromSync = false;
696 const bool kInstallSilently = true;
697 const bool kEnableOnInstall = true;
[email protected]a424d84c2010-09-24 09:31:15698 const bool kEnableIncognitoOnInstall = false;
[email protected]8ef78fd2010-08-19 17:14:32699
700 if (GetExtensionByIdInternal(id, true, true)) {
701 LOG(DFATAL) << "Trying to add extension " << id
702 << " by external update, but it is already installed.";
703 return;
704 }
705
706 AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync,
707 kInstallSilently, kEnableOnInstall,
[email protected]a424d84c2010-09-24 09:31:15708 kEnableIncognitoOnInstall);
[email protected]8ef78fd2010-08-19 17:14:32709}
710
[email protected]aa142702010-03-26 01:26:33711void ExtensionsService::AddPendingExtensionInternal(
712 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32713 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
714 bool is_from_sync, bool install_silently,
[email protected]4416c5a2010-06-26 01:28:57715 bool enable_on_install, bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33716 pending_extensions_[id] =
[email protected]8ef78fd2010-08-19 17:14:32717 PendingExtensionInfo(update_url, expected_crx_type, is_from_sync,
718 install_silently, enable_on_install,
719 enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33720}
721
[email protected]9cddd4702009-07-27 22:09:40722void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06723 FilePath path;
[email protected]61b411612009-11-10 23:17:41724 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40725
[email protected]f17dbd42010-08-16 23:21:10726 // Disable the extension if it's loaded. It might not be loaded if it crashed.
[email protected]b65272f2009-08-31 15:47:06727 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29728 // If the extension has an inspector open for its background page, detach
729 // the inspector and hang onto a cookie for it, so that we can reattach
730 // later.
731 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
732 ExtensionHost* host = manager->GetBackgroundHostForExtension(
733 current_extension);
734 if (host) {
735 // Look for an open inspector for the background page.
736 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
737 host->render_view_host());
738 if (devtools_cookie >= 0)
739 orphaned_dev_tools_[extension_id] = devtools_cookie;
740 }
741
[email protected]b65272f2009-08-31 15:47:06742 path = current_extension->path();
[email protected]f17dbd42010-08-16 23:21:10743 DisableExtension(extension_id);
744 disabled_extension_paths_[extension_id] = path;
[email protected]1eb175082010-02-10 09:26:16745 } else {
746 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06747 }
748
[email protected]e6090e42010-03-23 22:44:08749 // Check the installed extensions to see if what we're reloading was already
750 // installed.
751 scoped_ptr<ExtensionInfo> installed_extension(
752 extension_prefs_->GetInstalledExtensionInfo(extension_id));
753 if (installed_extension.get() &&
754 installed_extension->extension_manifest.get()) {
755 LoadInstalledExtension(*installed_extension, false);
756 } else {
757 // We should always be able to remember the extension's path. If it's not in
758 // the map, someone failed to update |unloaded_extension_paths_|.
759 CHECK(!path.empty());
760 LoadExtension(path);
761 }
[email protected]9cddd4702009-07-27 22:09:40762}
763
[email protected]27b985d2009-06-25 17:53:15764void ExtensionsService::UninstallExtension(const std::string& extension_id,
765 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39766 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25767
[email protected]e7afe2452010-08-22 16:19:13768 // Callers should not send us nonexistent extensions.
[email protected]e72e8eb82009-06-18 17:21:51769 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32770
[email protected]831aa212010-03-26 13:55:19771 // Get hold of information we need after unloading, since the extension
772 // pointer will be invalid then.
773 GURL extension_url(extension->url());
774 Extension::Location location(extension->location());
[email protected]211030342010-09-30 18:41:06775 UninstalledExtensionInfo uninstalled_extension_info(*extension);
[email protected]831aa212010-03-26 13:55:19776
777 // Also copy the extension identifier since the reference might have been
778 // obtained via Extension::id().
779 std::string extension_id_copy(extension_id);
780
[email protected]56ad3792010-05-28 17:45:33781 if (profile_->GetTemplateURLModel())
782 profile_->GetTemplateURLModel()->UnregisterExtensionKeyword(extension);
783
[email protected]831aa212010-03-26 13:55:19784 // Unload before doing more cleanup to ensure that nothing is hanging on to
785 // any of these resources.
786 UnloadExtension(extension_id);
787
788 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
789 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32790
791 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19792 if (Extension::LOAD != location) {
[email protected]95d29192009-10-30 01:49:06793 ChromeThread::PostTask(
794 ChromeThread::FILE, FROM_HERE,
795 NewRunnableFunction(
[email protected]ca3dbf52010-05-19 22:27:06796 &extension_file_util::UninstallExtension,
797 install_directory_,
798 extension_id_copy));
[email protected]9f1087e2009-06-15 17:29:32799 }
800
[email protected]c10da4b02010-03-25 14:38:32801 ClearExtensionData(extension_url);
[email protected]211030342010-09-30 18:41:06802
803 // Notify interested parties that we've uninstalled this extension.
804 NotificationService::current()->Notify(
805 NotificationType::EXTENSION_UNINSTALLED,
806 Source<Profile>(profile_),
807 Details<UninstalledExtensionInfo>(&uninstalled_extension_info));
[email protected]c10da4b02010-03-25 14:38:32808}
809
810void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
811 scoped_refptr<ExtensionDataDeleter> deleter(
812 new ExtensionDataDeleter(profile_, extension_url));
813 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32814}
815
[email protected]0c6da502009-08-14 22:32:39816void ExtensionsService::EnableExtension(const std::string& extension_id) {
817 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
818 if (!extension) {
[email protected]0c6da502009-08-14 22:32:39819 return;
820 }
821
[email protected]e8c729a2010-03-09 19:55:19822 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52823
[email protected]0c6da502009-08-14 22:32:39824 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39825 extensions_.push_back(extension);
826 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
827 disabled_extensions_.end(),
828 extension);
829 disabled_extensions_.erase(iter);
830
[email protected]86c008e82009-08-28 20:26:05831 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
832 extension->GetChromeURLOverrides());
833
[email protected]62d30f42009-10-01 22:36:06834 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35835 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39836}
837
[email protected]1784e83a2009-09-08 21:01:52838void ExtensionsService::DisableExtension(const std::string& extension_id) {
839 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15840 // The extension may have been disabled already.
841 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52842 return;
[email protected]1784e83a2009-09-08 21:01:52843
[email protected]e8c729a2010-03-09 19:55:19844 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52845
846 // Move it over to the disabled list.
847 disabled_extensions_.push_back(extension);
848 ExtensionList::iterator iter = std::find(extensions_.begin(),
849 extensions_.end(),
850 extension);
851 extensions_.erase(iter);
852
853 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
854 extension->GetChromeURLOverrides());
855
[email protected]62d30f42009-10-01 22:36:06856 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35857 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52858}
859
[email protected]9f1087e2009-06-15 17:29:32860void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06861 ChromeThread::PostTask(
862 ChromeThread::FILE, FROM_HERE,
863 NewRunnableMethod(
864 backend_.get(),
865 &ExtensionsServiceBackend::LoadSingleExtension,
866 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32867}
868
[email protected]1952c7d2010-03-04 23:48:34869void ExtensionsService::LoadComponentExtensions() {
870 for (RegisteredComponentExtensions::iterator it =
871 component_extension_manifests_.begin();
872 it != component_extension_manifests_.end(); ++it) {
873 JSONStringValueSerializer serializer(it->manifest);
[email protected]ba399672010-04-06 15:42:39874 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
[email protected]999731f2010-03-22 19:13:53875 if (!manifest.get()) {
[email protected]a94d57d2010-09-13 22:53:00876 DLOG(ERROR) << "Failed to parse manifest for extension";
[email protected]999731f2010-03-22 19:13:53877 continue;
878 }
[email protected]1952c7d2010-03-04 23:48:34879
880 scoped_ptr<Extension> extension(new Extension(it->root_directory));
881 extension->set_location(Extension::COMPONENT);
882
883 std::string error;
884 if (!extension->InitFromValue(
885 *static_cast<DictionaryValue*>(manifest.get()),
886 true, // require key
887 &error)) {
[email protected]4fdbc1492010-07-01 01:20:59888 NOTREACHED() << error;
[email protected]1952c7d2010-03-04 23:48:34889 return;
890 }
891
[email protected]fe13bf62010-08-26 14:33:19892 // In order for the --apps-gallery-url switch to work with the gallery
893 // process isolation, we must insert any provided value into the component
894 // app's launch url and web extent.
[email protected]7596ce72010-08-30 05:10:46895 if (extension->id() == extension_misc::kWebStoreAppId) {
[email protected]fe13bf62010-08-26 14:33:19896 GURL gallery_url(CommandLine::ForCurrentProcess()
897 ->GetSwitchValueASCII(switches::kAppsGalleryURL));
898 if (gallery_url.is_valid()) {
899 extension->set_launch_web_url(gallery_url.spec());
900 URLPattern pattern(URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS);
901 pattern.Parse(gallery_url.spec());
902 pattern.set_path(pattern.path() + '*');
903 extension->web_extent().AddPattern(pattern);
904 }
905 }
906
[email protected]1952c7d2010-03-04 23:48:34907 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
908 // increase.
909 }
910}
911
[email protected]9f1087e2009-06-15 17:29:32912void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36913 base::TimeTicks start_time = base::TimeTicks::Now();
914
[email protected]1952c7d2010-03-04 23:48:34915 // Load any component extensions.
916 LoadComponentExtensions();
917
[email protected]e72e8eb82009-06-18 17:21:51918 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06919 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08920 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06921
922 // If any extensions need localization, we bounce them all to the file thread
923 // for re-reading and localization.
924 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44925 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]c6d474f82009-12-16 21:11:06926 ChromeThread::PostTask(
927 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
928 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44929 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06930 info.release(), // Callee takes ownership of the memory.
931 start_time,
932 scoped_refptr<ExtensionsService>(this)));
933 return;
934 }
935 }
936
937 // Don't update prefs.
938 // Callee takes ownership of the memory.
939 ContinueLoadAllExtensions(info.release(), start_time, false);
940}
941
942void ExtensionsService::ContinueLoadAllExtensions(
943 ExtensionPrefs::ExtensionsInfo* extensions_info,
944 base::TimeTicks start_time,
945 bool write_to_prefs) {
946 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
947
948 for (size_t i = 0; i < info->size(); ++i) {
949 LoadInstalledExtension(*info->at(i), write_to_prefs);
950 }
951
[email protected]ae09ca62009-08-21 19:46:46952 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36953
954 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
955 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
956
[email protected]1952c7d2010-03-04 23:48:34957 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
958 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36959
[email protected]1952c7d2010-03-04 23:48:34960 int user_script_count = 0;
961 int extension_count = 0;
962 int theme_count = 0;
963 int external_count = 0;
964 int page_action_count = 0;
965 int browser_action_count = 0;
966 ExtensionList::iterator ex;
967 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
968 // Don't count component extensions, since they are only extensions as an
969 // implementation detail.
970 if ((*ex)->location() == Extension::COMPONENT)
971 continue;
972
[email protected]e8c729a2010-03-09 19:55:19973 // Don't count unpacked extensions, since they're a developer-specific
974 // feature.
975 if ((*ex)->location() == Extension::LOAD)
976 continue;
977
[email protected]3ba0fd32010-06-19 05:39:10978 if ((*ex)->is_theme()) {
[email protected]1952c7d2010-03-04 23:48:34979 theme_count++;
980 } else if ((*ex)->converted_from_user_script()) {
981 user_script_count++;
982 } else {
983 extension_count++;
[email protected]cc2c3432009-11-06 17:24:36984 }
[email protected]1952c7d2010-03-04 23:48:34985 if (Extension::IsExternalLocation((*ex)->location())) {
986 external_count++;
987 }
988 if ((*ex)->page_action() != NULL) {
989 page_action_count++;
990 }
991 if ((*ex)->browser_action() != NULL) {
992 browser_action_count++;
993 }
[email protected]cc2c3432009-11-06 17:24:36994 }
[email protected]1952c7d2010-03-04 23:48:34995 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
996 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
997 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
998 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
999 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
1000 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
1001 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:461002}
1003
[email protected]c6d474f82009-12-16 21:11:061004void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
1005 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:461006 std::string error;
1007 Extension* extension = NULL;
[email protected]306a2bd2010-08-11 14:56:361008 if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) {
1009 error = errors::kDisabledByPolicy;
1010 } else if (info.extension_manifest.get()) {
[email protected]c6d474f82009-12-16 21:11:061011 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:191012 bool require_key = info.extension_location != Extension::LOAD;
1013 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:461014 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:461015 } else {
[email protected]c6d474f82009-12-16 21:11:061016 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:461017 }
1018
1019 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:061020 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:401021 error,
1022 NotificationType::EXTENSION_INSTALL_ERROR,
1023 false);
[email protected]ae09ca62009-08-21 19:46:461024 return;
1025 }
1026
[email protected]c6d474f82009-12-16 21:11:061027 extension->set_location(info.extension_location);
1028
1029 if (write_to_prefs)
1030 extension_prefs_->UpdateManifest(extension);
1031
[email protected]2a409532009-08-28 19:39:441032 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:461033
[email protected]55196e92010-09-29 15:04:461034 if (Extension::IsExternalLocation(info.extension_location)) {
[email protected]95d29192009-10-30 01:49:061035 ChromeThread::PostTask(
1036 ChromeThread::FILE, FROM_HERE,
1037 NewRunnableMethod(
[email protected]f17dbd42010-08-16 23:21:101038 backend_.get(),
1039 &ExtensionsServiceBackend::CheckExternalUninstall,
1040 scoped_refptr<ExtensionsService>(this),
1041 info.extension_id,
1042 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:461043 }
[email protected]9f1087e2009-06-15 17:29:321044}
1045
[email protected]62d30f42009-10-01 22:36:061046void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
[email protected]57a777f72010-03-31 01:09:421047 // The ChromeURLRequestContexts need to be first to know that the extension
[email protected]62d30f42009-10-01 22:36:061048 // was loaded, otherwise a race can arise where a renderer that is created
1049 // for the extension may try to load an extension URL with an extension id
[email protected]57a777f72010-03-31 01:09:421050 // that the request context doesn't yet know about. The profile is responsible
1051 // for ensuring its URLRequestContexts appropriately discover the loaded
1052 // extension.
1053 if (profile_) {
1054 profile_->RegisterExtensionWithRequestContexts(extension);
[email protected]24b538a2010-02-27 01:22:441055
1056 // Check if this permission requires unlimited storage quota
[email protected]c2c263c2010-08-13 21:59:481057 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
1058 GrantUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:421059
1060 // If the extension is an app, protect its local storage from
1061 // "Clear browsing data."
1062 if (extension->is_app())
1063 GrantProtectedStorage(extension);
[email protected]62d30f42009-10-01 22:36:061064 }
1065
[email protected]62d30f42009-10-01 22:36:061066 NotificationService::current()->Notify(
1067 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:341068 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:061069 Details<Extension>(extension));
1070}
1071
1072void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
[email protected]62d30f42009-10-01 22:36:061073 NotificationService::current()->Notify(
1074 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:341075 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:061076 Details<Extension>(extension));
1077
[email protected]57a777f72010-03-31 01:09:421078 if (profile_) {
1079 profile_->UnregisterExtensionWithRequestContexts(extension);
1080
1081 // Check if this permission required unlimited storage quota, reset its
1082 // in-memory quota.
[email protected]c2c263c2010-08-13 21:59:481083 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
1084 RevokeUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:421085
1086 // If this is an app, then stop protecting its storage so it can be deleted.
1087 if (extension->is_app())
1088 RevokeProtectedStorage(extension);
1089 }
1090}
1091
1092void ExtensionsService::GrantProtectedStorage(Extension* extension) {
1093 DCHECK(extension->is_app()) << "Only Apps are allowed protected storage.";
1094 std::vector<GURL> origins;
1095 GetExplicitOriginsInExtent(extension, &origins);
1096 for (size_t i = 0; i < origins.size(); ++i)
1097 ++protected_storage_map_[origins[i]];
1098}
1099
1100void ExtensionsService::RevokeProtectedStorage(Extension* extension) {
1101 DCHECK(extension->is_app()) << "Attempting to revoke protected storage from "
1102 << " a non-app extension.";
1103 std::vector<GURL> origins;
1104 GetExplicitOriginsInExtent(extension, &origins);
1105 for (size_t i = 0; i < origins.size(); ++i) {
1106 const GURL& origin = origins[i];
1107 DCHECK(protected_storage_map_[origin] > 0);
1108 if (--protected_storage_map_[origin] <= 0)
1109 protected_storage_map_.erase(origin);
[email protected]c2c263c2010-08-13 21:59:481110 }
1111}
1112
1113void ExtensionsService::GrantUnlimitedStorage(Extension* extension) {
1114 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
1115 std::vector<GURL> origins;
1116 GetExplicitOriginsInExtent(extension, &origins);
1117 origins.push_back(extension->url());
1118
1119 for (size_t i = 0; i < origins.size(); ++i) {
1120 const GURL& origin = origins[i];
1121 if (++unlimited_storage_map_[origin] == 1) {
[email protected]57a777f72010-03-31 01:09:421122 string16 origin_identifier =
[email protected]c2c263c2010-08-13 21:59:481123 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
1124 ChromeThread::PostTask(
1125 ChromeThread::FILE, FROM_HERE,
1126 NewRunnableMethod(
1127 profile_->GetDatabaseTracker(),
1128 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
1129 origin_identifier,
1130 kint64max));
1131 ChromeThread::PostTask(
1132 ChromeThread::IO, FROM_HERE,
1133 NewRunnableMethod(
1134 profile_->GetAppCacheService(),
1135 &ChromeAppCacheService::SetOriginQuotaInMemory,
1136 origin,
1137 kint64max));
1138 }
1139 }
1140}
1141
1142void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) {
1143 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
1144 std::vector<GURL> origins;
1145 GetExplicitOriginsInExtent(extension, &origins);
1146 origins.push_back(extension->url());
1147
1148 for (size_t i = 0; i < origins.size(); ++i) {
1149 const GURL& origin = origins[i];
1150 DCHECK(unlimited_storage_map_[origin] > 0);
1151 if (--unlimited_storage_map_[origin] == 0) {
1152 unlimited_storage_map_.erase(origin);
1153 string16 origin_identifier =
1154 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
[email protected]95d29192009-10-30 01:49:061155 ChromeThread::PostTask(
[email protected]57a777f72010-03-31 01:09:421156 ChromeThread::FILE, FROM_HERE,
[email protected]be180c802009-10-23 06:33:311157 NewRunnableMethod(
[email protected]57a777f72010-03-31 01:09:421158 profile_->GetDatabaseTracker(),
1159 &webkit_database::DatabaseTracker::ResetOriginQuotaInMemory,
1160 origin_identifier));
[email protected]c2c263c2010-08-13 21:59:481161 ChromeThread::PostTask(
1162 ChromeThread::IO, FROM_HERE,
1163 NewRunnableMethod(
1164 profile_->GetAppCacheService(),
1165 &ChromeAppCacheService::ResetOriginQuotaInMemory,
1166 origin));
[email protected]62d30f42009-10-01 22:36:061167 }
1168 }
1169}
1170
[email protected]6b75ec32009-08-14 06:37:181171void ExtensionsService::UpdateExtensionBlacklist(
1172 const std::vector<std::string>& blacklist) {
1173 // Use this set to indicate if an extension in the blacklist has been used.
1174 std::set<std::string> blacklist_set;
1175 for (unsigned int i = 0; i < blacklist.size(); ++i) {
1176 if (Extension::IdIsValid(blacklist[i])) {
1177 blacklist_set.insert(blacklist[i]);
1178 }
1179 }
1180 extension_prefs_->UpdateBlacklist(blacklist_set);
1181 std::vector<std::string> to_be_removed;
1182 // Loop current extensions, unload installed extensions.
1183 for (ExtensionList::const_iterator iter = extensions_.begin();
1184 iter != extensions_.end(); ++iter) {
1185 Extension* extension = (*iter);
1186 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
1187 to_be_removed.push_back(extension->id());
1188 }
1189 }
1190
1191 // UnloadExtension will change the extensions_ list. So, we should
1192 // call it outside the iterator loop.
1193 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
1194 UnloadExtension(to_be_removed[i]);
1195 }
1196}
1197
[email protected]aa96d3a2010-08-21 08:45:251198void ExtensionsService::DestroyingProfile() {
[email protected]2fb7dc982010-09-29 12:24:281199 pref_change_registrar_.RemoveAll();
[email protected]aa96d3a2010-08-21 08:45:251200 profile_ = NULL;
[email protected]2fb7dc982010-09-29 12:24:281201 toolbar_model_.DestroyingProfile();
[email protected]aa96d3a2010-08-21 08:45:251202}
1203
1204void ExtensionsService::CheckAdminBlacklist() {
1205 std::vector<std::string> to_be_removed;
1206 // Loop through extensions list, unload installed extensions.
1207 for (ExtensionList::const_iterator iter = extensions_.begin();
1208 iter != extensions_.end(); ++iter) {
1209 Extension* extension = (*iter);
1210 if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id()))
1211 to_be_removed.push_back(extension->id());
1212 }
1213
1214 // UnloadExtension will change the extensions_ list. So, we should
1215 // call it outside the iterator loop.
1216 for (unsigned int i = 0; i < to_be_removed.size(); ++i)
1217 UnloadExtension(to_be_removed[i]);
1218}
1219
[email protected]cb0ce1e022010-03-10 19:54:411220bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
1221 // If this is a component extension we always allow it to work in incognito
1222 // mode.
1223 if (extension->location() == Extension::COMPONENT)
1224 return true;
1225
1226 // Check the prefs.
1227 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:501228}
[email protected]55a35692010-02-11 23:25:211229
[email protected]cb0ce1e022010-03-10 19:54:411230void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:501231 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:411232 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:241233
[email protected]568f33d2010-08-04 17:06:411234 // Broadcast unloaded and loaded events to update browser state. Only bother
1235 // if the extension is actually enabled, since there is no UI otherwise.
1236 bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
1237 extension) != extensions_.end();
1238 if (is_enabled) {
1239 NotifyExtensionUnloaded(extension);
1240 NotifyExtensionLoaded(extension);
1241 }
[email protected]55a35692010-02-11 23:25:211242}
1243
[email protected]05c82182010-06-24 17:49:081244bool ExtensionsService::AllowFileAccess(const Extension* extension) {
1245 return (CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]334e04a2010-06-24 23:34:441246 switches::kDisableExtensionsFileAccessCheck) ||
[email protected]05c82182010-06-24 17:49:081247 extension_prefs_->AllowFileAccess(extension->id()));
1248}
1249
1250void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
1251 extension_prefs_->SetAllowFileAccess(extension->id(), allow);
1252 NotificationService::current()->Notify(
1253 NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
1254 Source<Profile>(profile_),
1255 Details<Extension>(extension));
1256}
1257
1258bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
1259 const GURL& url,
1260 std::string* error) const {
1261 // No extensions are allowed to execute script on the gallery because that
1262 // would allow extensions to manipulate their own install pages.
[email protected]638d45d2010-07-09 17:35:081263 if (url.host() == GURL(Extension::ChromeStoreURL()).host()
1264 && !CommandLine::ForCurrentProcess()->HasSwitch(
1265 switches::kAllowScriptingGallery)) {
[email protected]05c82182010-06-24 17:49:081266 if (error)
1267 *error = errors::kCannotScriptGallery;
1268 return false;
1269 }
1270
1271 if (extension->HasHostPermission(url))
1272 return true;
1273
1274 if (error) {
1275 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
1276 url.spec());
1277 }
1278
1279 return false;
1280}
1281
[email protected]93fd78f42009-07-10 16:43:171282void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:321283 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:581284 // TODO(aa): Why pass this list into the provider, why not just filter it
1285 // later?
[email protected]9f1087e2009-06-15 17:29:321286 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:511287 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:061288 ChromeThread::PostTask(
1289 ChromeThread::FILE, FROM_HERE,
1290 NewRunnableMethod(
1291 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
1292 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:321293}
1294
1295void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:091296 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:391297 scoped_ptr<Extension> extension(
1298 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:251299
[email protected]e7afe2452010-08-22 16:19:131300 // Callers should not send us nonexistent extensions.
[email protected]0c6da502009-08-14 22:32:391301 CHECK(extension.get());
1302
[email protected]1eb175082010-02-10 09:26:161303 // Keep information about the extension so that we can reload it later
1304 // even if it's not permanently installed.
1305 unloaded_extension_paths_[extension->id()] = extension->path();
1306
[email protected]f17dbd42010-08-16 23:21:101307 // Clean up if the extension is meant to be enabled after a reload.
1308 disabled_extension_paths_.erase(extension->id());
1309
[email protected]86c008e82009-08-28 20:26:051310 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
1311 extension->GetChromeURLOverrides());
1312
[email protected]0c6da502009-08-14 22:32:391313 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
1314 disabled_extensions_.end(),
1315 extension.get());
1316 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:391317 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:471318 NotificationService::current()->Notify(
1319 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341320 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:471321 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:391322 return;
1323 }
1324
1325 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:571326
[email protected]631cf822009-05-15 07:01:251327 // Remove the extension from our list.
1328 extensions_.erase(iter);
1329
[email protected]62d30f42009-10-01 22:36:061330 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:351331 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:251332}
1333
[email protected]9f1087e2009-06-15 17:29:321334void ExtensionsService::UnloadAllExtensions() {
[email protected]cd500f72010-06-25 23:44:321335 STLDeleteContainerPointers(extensions_.begin(), extensions_.end());
[email protected]9f1087e2009-06-15 17:29:321336 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:291337
[email protected]cd500f72010-06-25 23:44:321338 STLDeleteContainerPointers(disabled_extensions_.begin(),
1339 disabled_extensions_.end());
1340 disabled_extensions_.clear();
1341
[email protected]c6e4a3412009-06-24 15:45:291342 // TODO(erikkay) should there be a notification for this? We can't use
1343 // EXTENSION_UNLOADED since that implies that the extension has been disabled
1344 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:321345}
1346
1347void ExtensionsService::ReloadExtensions() {
1348 UnloadAllExtensions();
1349 LoadAllExtensions();
1350}
1351
1352void ExtensionsService::GarbageCollectExtensions() {
[email protected]ba399672010-04-06 15:42:391353 if (extension_prefs_->pref_service()->read_only())
1354 return;
1355
[email protected]ca3dbf52010-05-19 22:27:061356 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
1357 extension_prefs_->GetInstalledExtensionsInfo());
1358
1359 std::map<std::string, FilePath> extension_paths;
1360 for (size_t i = 0; i < info->size(); ++i)
1361 extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
1362
[email protected]95d29192009-10-30 01:49:061363 ChromeThread::PostTask(
1364 ChromeThread::FILE, FROM_HERE,
1365 NewRunnableFunction(
1366 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]ca3dbf52010-05-19 22:27:061367 extension_paths));
[email protected]3cf4f0992009-02-03 23:00:301368}
1369
[email protected]e72e8eb82009-06-18 17:21:511370void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:131371 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:171372 if (updater_.get()) {
1373 updater_->Start();
1374 }
[email protected]e72e8eb82009-06-18 17:21:511375 NotificationService::current()->Notify(
1376 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:341377 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:511378 NotificationService::NoDetails());
1379}
1380
[email protected]6d2e60bd2010-06-03 22:37:391381void ExtensionsService::OnExtensionLoaded(Extension* extension,
[email protected]2a409532009-08-28 19:39:441382 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:461383 // Ensure extension is deleted unless we transfer ownership.
1384 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:321385
[email protected]1eb175082010-02-10 09:26:161386 // The extension is now loaded, remove its data from unloaded extension map.
1387 unloaded_extension_paths_.erase(extension->id());
1388
[email protected]f17dbd42010-08-16 23:21:101389 // If the extension was disabled for a reload, then enable it.
1390 if (disabled_extension_paths_.erase(extension->id()) > 0)
1391 EnableExtension(extension->id());
1392
[email protected]ceefd3d2010-03-12 09:10:291393 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
1394 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:461395 if (extensions_enabled() ||
[email protected]3ba0fd32010-06-19 05:39:101396 extension->is_theme() ||
[email protected]ae09ca62009-08-21 19:46:461397 extension->location() == Extension::LOAD ||
1398 Extension::IsExternalLocation(extension->location())) {
1399 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
1400 if (old) {
[email protected]ca3dbf52010-05-19 22:27:061401 // CrxInstaller should have guaranteed that we aren't downgrading.
1402 CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
[email protected]0c6da502009-08-14 22:32:391403
[email protected]ca3dbf52010-05-19 22:27:061404 bool allow_silent_upgrade =
1405 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
1406 old, extension);
[email protected]1e8c93f2010-02-08 22:58:311407
[email protected]ca3dbf52010-05-19 22:27:061408 // Extensions get upgraded if silent upgrades are allowed, otherwise
1409 // they get disabled.
1410 if (allow_silent_upgrade) {
1411 old->set_being_upgraded(true);
1412 extension->set_being_upgraded(true);
1413 }
[email protected]0c6da502009-08-14 22:32:391414
[email protected]ca3dbf52010-05-19 22:27:061415 // To upgrade an extension in place, unload the old one and
1416 // then load the new one.
1417 UnloadExtension(old->id());
1418 old = NULL;
1419
1420 if (!allow_silent_upgrade) {
1421 // Extension has changed permissions significantly. Disable it. We
1422 // send a notification below.
1423 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
1424 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]0c6da502009-08-14 22:32:391425 }
[email protected]ba74f352009-06-11 18:54:451426 }
[email protected]86a274072009-06-11 02:06:451427
[email protected]ae09ca62009-08-21 19:46:461428 switch (extension_prefs_->GetExtensionState(extension->id())) {
1429 case Extension::ENABLED:
1430 extensions_.push_back(scoped_extension.release());
1431
[email protected]62d30f42009-10-01 22:36:061432 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:461433
[email protected]e8c729a2010-03-09 19:55:191434 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
1435 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:461436 break;
1437 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:451438 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:401439 NotificationService::current()->Notify(
1440 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341441 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401442 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:461443 break;
1444 default:
[email protected]d11c8e92009-10-20 23:26:401445 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:461446 break;
[email protected]811f3432009-07-25 19:38:211447 }
[email protected]e72e8eb82009-06-18 17:21:511448 }
[email protected]aab98a52009-12-02 03:22:351449
[email protected]1e8c93f2010-02-08 22:58:311450 extension->set_being_upgraded(false);
1451
[email protected]aab98a52009-12-02 03:22:351452 UpdateActiveExtensionsInCrashReporter();
[email protected]0b004da2010-07-02 17:54:311453
1454 if (profile_->GetTemplateURLModel())
1455 profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension);
[email protected]b671760b2010-07-15 21:13:471456
1457 // Load the icon for omnibox-enabled extensions so it will be ready to display
1458 // in the URL bar.
[email protected]29d0d4ac2010-09-08 21:10:311459 if (!extension->omnibox_keyword().empty()) {
1460 omnibox_popup_icon_manager_.LoadIcon(extension);
[email protected]b671760b2010-07-15 21:13:471461 omnibox_icon_manager_.LoadIcon(extension);
[email protected]29d0d4ac2010-09-08 21:10:311462 }
[email protected]aab98a52009-12-02 03:22:351463}
1464
1465void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:391466 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:351467 for (size_t i = 0; i < extensions_.size(); ++i) {
[email protected]3ba0fd32010-06-19 05:39:101468 if (!extensions_[i]->is_theme())
[email protected]c8865962009-12-16 07:47:391469 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:351470 }
1471
1472 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:251473}
1474
[email protected]2a409532009-08-28 19:39:441475void ExtensionsService::OnExtensionInstalled(Extension* extension,
1476 bool allow_privilege_increase) {
[email protected]4416c5a2010-06-26 01:28:571477 // Ensure extension is deleted unless we transfer ownership.
1478 scoped_ptr<Extension> scoped_extension(extension);
1479 Extension::State initial_state = Extension::DISABLED;
1480 bool initial_enable_incognito = false;
[email protected]aa142702010-03-26 01:26:331481 PendingExtensionMap::iterator it =
1482 pending_extensions_.find(extension->id());
[email protected]4416c5a2010-06-26 01:28:571483 if (it != pending_extensions_.end()) {
[email protected]11edd1e2010-07-21 00:14:501484 PendingExtensionInfo pending_extension_info = it->second;
[email protected]8ef78fd2010-08-19 17:14:321485 PendingExtensionInfo::ExpectedCrxType expected_crx_type =
1486 pending_extension_info.expected_crx_type;
1487 bool is_from_sync = pending_extension_info.is_from_sync;
[email protected]11edd1e2010-07-21 00:14:501488 pending_extensions_.erase(it);
1489 it = pending_extensions_.end();
[email protected]8ef78fd2010-08-19 17:14:321490
[email protected]4416c5a2010-06-26 01:28:571491 // Set initial state from pending extension data.
[email protected]8ef78fd2010-08-19 17:14:321492 PendingExtensionInfo::ExpectedCrxType actual_crx_type =
1493 (extension->is_theme() ? PendingExtensionInfo::THEME
1494 : PendingExtensionInfo::EXTENSION);
1495
1496 if (expected_crx_type != PendingExtensionInfo::UNKNOWN &&
1497 expected_crx_type != actual_crx_type) {
[email protected]4416c5a2010-06-26 01:28:571498 LOG(WARNING)
1499 << "Not installing pending extension " << extension->id()
[email protected]8ef78fd2010-08-19 17:14:321500 << " with is_theme = " << extension->is_theme();
[email protected]4416c5a2010-06-26 01:28:571501 // Delete the extension directory since we're not going to
1502 // load it.
1503 ChromeThread::PostTask(
1504 ChromeThread::FILE, FROM_HERE,
1505 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1506 return;
1507 }
[email protected]8ef78fd2010-08-19 17:14:321508
1509 // If |extension| is not syncable, and was installed via sync, disallow
1510 // the instanation.
1511 //
1512 // Themes are always allowed. Because they contain no active code, they
1513 // are less of a risk than extensions.
1514 //
1515 // If |is_from_sync| is false, then the install was not initiated by sync,
1516 // and this check should pass. Extensions that were installed from an
1517 // update URL in external_extensions.json are an example. They are not
1518 // syncable, because the user did not make an explicit choice to install
1519 // them. However, they were installed through the update mechanism, so
1520 // control must pass into this function.
1521 //
1522 // TODO(akalin): When we do apps sync, we have to work with its
1523 // traits, too.
[email protected]2a3e22b12010-08-13 04:55:171524 const browser_sync::ExtensionSyncTraits extension_sync_traits =
1525 browser_sync::GetExtensionSyncTraits();
[email protected]06e33202010-08-16 23:45:151526 const browser_sync::ExtensionSyncTraits app_sync_traits =
1527 browser_sync::GetAppSyncTraits();
[email protected]2a3e22b12010-08-13 04:55:171528 // If an extension is a theme, we bypass the valid/syncable check
1529 // as themes are harmless.
[email protected]8ef78fd2010-08-19 17:14:321530 if (!extension->is_theme() && is_from_sync &&
[email protected]2a3e22b12010-08-13 04:55:171531 !browser_sync::IsExtensionValidAndSyncable(
[email protected]06e33202010-08-16 23:45:151532 *extension, extension_sync_traits.allowed_extension_types) &&
1533 !browser_sync::IsExtensionValidAndSyncable(
1534 *extension, app_sync_traits.allowed_extension_types)) {
[email protected]11edd1e2010-07-21 00:14:501535 // We're an extension installed via sync that is unsyncable,
1536 // i.e. we may have been syncable previously. We block these
1537 // installs. We'll have to update the clause above if we decide
1538 // to sync other extension-like things, like apps or user
1539 // scripts.
1540 //
1541 // Note that this creates a small window where a user who tries
1542 // to download/install an extension that is simultaneously
1543 // installed via sync (and blocked) will find his download
1544 // blocked.
1545 //
1546 // TODO(akalin): Remove this check once we've put in UI to
1547 // approve synced extensions.
1548 LOG(WARNING)
[email protected]2a3e22b12010-08-13 04:55:171549 << "Not installing invalid or unsyncable extension "
1550 << extension->id();
[email protected]11edd1e2010-07-21 00:14:501551 // Delete the extension directory since we're not going to
1552 // load it.
1553 ChromeThread::PostTask(
1554 ChromeThread::FILE, FROM_HERE,
1555 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1556 return;
1557 }
[email protected]8ef78fd2010-08-19 17:14:321558 if (extension->is_theme()) {
[email protected]11edd1e2010-07-21 00:14:501559 DCHECK(pending_extension_info.enable_on_install);
[email protected]4416c5a2010-06-26 01:28:571560 initial_state = Extension::ENABLED;
[email protected]11edd1e2010-07-21 00:14:501561 DCHECK(!pending_extension_info.enable_incognito_on_install);
[email protected]4416c5a2010-06-26 01:28:571562 initial_enable_incognito = false;
1563 } else {
1564 initial_state =
[email protected]11edd1e2010-07-21 00:14:501565 pending_extension_info.enable_on_install ?
[email protected]4416c5a2010-06-26 01:28:571566 Extension::ENABLED : Extension::DISABLED;
1567 initial_enable_incognito =
[email protected]11edd1e2010-07-21 00:14:501568 pending_extension_info.enable_incognito_on_install;
[email protected]4416c5a2010-06-26 01:28:571569 }
[email protected]4416c5a2010-06-26 01:28:571570 } else {
[email protected]dbec3792010-08-10 00:08:451571 // Make sure we preserve enabled/disabled states.
[email protected]4416c5a2010-06-26 01:28:571572 Extension::State existing_state =
1573 extension_prefs_->GetExtensionState(extension->id());
1574 initial_state =
1575 (existing_state == Extension::DISABLED) ?
1576 Extension::DISABLED : Extension::ENABLED;
[email protected]dbec3792010-08-10 00:08:451577 initial_enable_incognito =
1578 extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]aa142702010-03-26 01:26:331579 }
1580
[email protected]4416c5a2010-06-26 01:28:571581 extension_prefs_->OnExtensionInstalled(
1582 extension, initial_state, initial_enable_incognito);
[email protected]25b34332009-06-05 21:53:191583
[email protected]92a5b1d2010-07-20 00:42:001584 // Unpacked extensions start off with file access since they are a developer
1585 // feature.
1586 if (extension->location() == Extension::LOAD)
1587 extension_prefs_->SetAllowFileAccess(extension->id(), true);
1588
[email protected]4a190632009-05-09 01:07:421589 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
1590 // to apply it.
[email protected]3ba0fd32010-06-19 05:39:101591 if (extension->is_theme()) {
[email protected]9ceb07342009-07-26 04:09:231592 NotificationService::current()->Notify(
1593 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341594 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:231595 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:271596 } else {
1597 NotificationService::current()->Notify(
1598 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341599 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:271600 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:421601 }
[email protected]7577a5c52009-07-30 06:21:581602
[email protected]4416c5a2010-06-26 01:28:571603 // Transfer ownership of |extension| to OnExtensionLoaded.
1604 OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:421605}
1606
[email protected]0c6da502009-08-14 22:32:391607Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
1608 bool include_enabled,
1609 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:051610 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:391611 if (include_enabled) {
1612 for (ExtensionList::const_iterator iter = extensions_.begin();
1613 iter != extensions_.end(); ++iter) {
1614 if ((*iter)->id() == lowercase_id)
1615 return *iter;
1616 }
1617 }
1618 if (include_disabled) {
1619 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
1620 iter != disabled_extensions_.end(); ++iter) {
1621 if ((*iter)->id() == lowercase_id)
1622 return *iter;
1623 }
[email protected]ce5c4502009-05-06 16:46:111624 }
1625 return NULL;
1626}
1627
[email protected]9f1087e2009-06-15 17:29:321628Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
[email protected]a888b29e62010-04-01 13:38:571629 return url.scheme() != chrome::kExtensionScheme ? NULL :
1630 GetExtensionById(url.host(), false);
1631}
1632
1633Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) {
1634 for (size_t i = 0; i < extensions_.size(); ++i) {
1635 if (extensions_[i]->web_extent().ContainsURL(url))
1636 return extensions_[i];
1637 }
1638 return NULL;
[email protected]9f1087e2009-06-15 17:29:321639}
1640
[email protected]583d45c12010-08-31 02:48:121641bool ExtensionsService::ExtensionBindingsAllowed(const GURL& url) {
1642 // Allow bindings for all packaged extension.
1643 if (GetExtensionByURL(url))
1644 return true;
1645
1646 // Allow bindings for all component, hosted apps.
1647 Extension* extension = GetExtensionByWebExtent(url);
1648 return (extension && extension->location() == Extension::COMPONENT);
1649}
1650
[email protected]6d2e60bd2010-06-03 22:37:391651Extension* ExtensionsService::GetExtensionByOverlappingWebExtent(
[email protected]9f72aa02010-06-25 10:01:051652 const ExtensionExtent& extent) {
[email protected]22c966c2010-06-26 06:35:021653 for (size_t i = 0; i < extensions_.size(); ++i) {
1654 if (extensions_[i]->web_extent().OverlapsWith(extent))
1655 return extensions_[i];
1656 }
1657
[email protected]6d2e60bd2010-06-03 22:37:391658 return NULL;
1659}
1660
[email protected]b671760b2010-07-15 21:13:471661const SkBitmap& ExtensionsService::GetOmniboxIcon(
1662 const std::string& extension_id) {
1663 return omnibox_icon_manager_.GetIcon(extension_id);
1664}
1665
[email protected]29d0d4ac2010-09-08 21:10:311666const SkBitmap& ExtensionsService::GetOmniboxPopupIcon(
1667 const std::string& extension_id) {
1668 return omnibox_popup_icon_manager_.GetIcon(extension_id);
1669}
1670
[email protected]a1257b12009-06-12 02:51:341671void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:061672 ChromeThread::PostTask(
1673 ChromeThread::FILE, FROM_HERE,
1674 NewRunnableMethod(
1675 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:341676}
1677
1678void ExtensionsService::SetProviderForTesting(
1679 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:061680 ChromeThread::PostTask(
1681 ChromeThread::FILE, FROM_HERE,
1682 NewRunnableMethod(
1683 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
1684 location, test_provider));
[email protected]a1257b12009-06-12 02:51:341685}
1686
[email protected]8ef78fd2010-08-19 17:14:321687void ExtensionsService::OnExternalExtensionFileFound(
1688 const std::string& id,
1689 const std::string& version,
1690 const FilePath& path,
1691 Extension::Location location) {
[email protected]7577a5c52009-07-30 06:21:581692 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:491693 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:581694 // installed on every startup.
[email protected]61b411612009-11-10 23:17:411695 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:111696 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:581697 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:111698 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:581699 case -1: // existing version is older, we should upgrade
1700 break;
1701 case 0: // existing version is same, do nothing
1702 return;
1703 case 1: // existing version is newer, uh-oh
1704 LOG(WARNING) << "Found external version of extension " << id
1705 << "that is older than current version. Current version "
1706 << "is: " << existing->VersionString() << ". New version "
1707 << "is: " << version << ". Keeping current version.";
1708 return;
1709 }
1710 }
1711
[email protected]6dfbbf82010-03-12 23:09:161712 scoped_refptr<CrxInstaller> installer(
1713 new CrxInstaller(install_directory_,
1714 this, // frontend
1715 NULL)); // no client (silent install)
1716 installer->set_install_source(location);
1717 installer->set_expected_id(id);
1718 installer->set_allow_privilege_increase(true);
1719 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581720}
1721
[email protected]d11c8e92009-10-20 23:26:401722void ExtensionsService::ReportExtensionLoadError(
1723 const FilePath& extension_path,
1724 const std::string &error,
1725 NotificationType type,
1726 bool be_noisy) {
1727 NotificationService* service = NotificationService::current();
1728 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341729 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401730 Details<const std::string>(&error));
1731
1732 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161733 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]18d4b6c2010-09-21 03:21:041734 std::string message = base::StringPrintf(
1735 "Could not load extension from '%s'. %s",
1736 path_str.c_str(), error.c_str());
[email protected]d11c8e92009-10-20 23:26:401737 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1738}
1739
[email protected]406027c02010-09-27 08:03:181740void ExtensionsService::DidCreateRenderViewForBackgroundPage(
1741 ExtensionHost* host) {
1742 OrphanedDevTools::iterator iter =
1743 orphaned_dev_tools_.find(host->extension()->id());
1744 if (iter == orphaned_dev_tools_.end())
1745 return;
1746
1747 DevToolsManager::GetInstance()->AttachClientHost(
1748 iter->second, host->render_view_host());
1749 orphaned_dev_tools_.erase(iter);
1750}
1751
[email protected]4814b512009-11-07 00:12:291752void ExtensionsService::Observe(NotificationType type,
1753 const NotificationSource& source,
1754 const NotificationDetails& details) {
1755 switch (type.value) {
[email protected]a4ed6282009-12-14 20:51:161756 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]bc535ee52010-08-31 18:40:321757 if (profile_ != Source<Profile>(source).ptr()->GetOriginalProfile())
1758 break;
[email protected]a4ed6282009-12-14 20:51:161759
[email protected]f128af42010-08-05 18:05:261760 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1761
1762 // TODO(rafaelw): Remove this check and ExtensionHost::recently_deleted().
1763 // This is only here to help track down crbug.com/49114.
1764 ExtensionHost::HostPointerList::iterator iter =
1765 ExtensionHost::recently_deleted()->begin();
1766 for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
1767 if (*iter == host) {
1768 CHECK(host->GetURL().spec().size() + 2 != 0);
1769 break;
1770 }
1771 }
1772 if (iter == ExtensionHost::recently_deleted()->end())
1773 CHECK(host->GetURL().spec().size() + 1 != 0);
1774
[email protected]31f77262009-12-02 20:48:531775 // Unload the entire extension. We want it to be in a consistent state:
1776 // either fully working or not loaded at all, but never half-crashed.
[email protected]bc535ee52010-08-31 18:40:321777 // We do it in a PostTask so that other handlers of this notification will
1778 // still have access to the Extension and ExtensionHost.
1779 MessageLoop::current()->PostTask(FROM_HERE,
1780 NewRunnableMethod(this, &ExtensionsService::UnloadExtension,
1781 host->extension()->id()));
[email protected]31f77262009-12-02 20:48:531782 break;
1783 }
1784
[email protected]aa96d3a2010-08-21 08:45:251785 case NotificationType::PREF_CHANGED: {
1786 std::string* pref_name = Details<std::string>(details).ptr();
1787 DCHECK(*pref_name == prefs::kExtensionInstallAllowList ||
1788 *pref_name == prefs::kExtensionInstallDenyList);
1789 CheckAdminBlacklist();
1790 break;
1791 }
1792
[email protected]4814b512009-11-07 00:12:291793 default:
1794 NOTREACHED() << "Unexpected notification type.";
1795 }
1796}
1797
[email protected]377011d2010-07-20 04:18:501798bool ExtensionsService::HasApps() {
1799 if (!extensions_enabled_)
1800 return false;
1801
1802 for (ExtensionList::const_iterator it = extensions_.begin();
1803 it != extensions_.end(); ++it) {
1804 if ((*it)->is_app())
1805 return true;
1806 }
1807
1808 return false;
1809}