blob: bfdc6fa409381c1b2e38c6a1e7315b99f45a3687 [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]24b538a2010-02-27 01:22:447#include "base/basictypes.h"
[email protected]e2eb43112009-05-29 21:19:548#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:259#include "base/file_util.h"
[email protected]cc2c3432009-11-06 17:24:3610#include "base/histogram.h"
[email protected]cd500f72010-06-25 23:44:3211#include "base/stl_util-inl.h"
[email protected]24b538a2010-02-27 01:22:4412#include "base/string16.h"
[email protected]e83326f2010-07-31 17:29:2513#include "base/string_number_conversions.h"
[email protected]6014d672008-12-05 00:38:2514#include "base/string_util.h"
[email protected]cc2c3432009-11-06 17:24:3615#include "base/time.h"
[email protected]cc655912009-01-29 23:19:1916#include "base/values.h"
[email protected]aa142702010-03-26 01:26:3317#include "base/version.h"
[email protected]15730c42009-09-03 00:03:2018#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2119#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2920#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5821#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5622#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3823#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2824#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]2c5e1e12010-06-10 13:14:4425#include "chrome/browser/extensions/extension_cookies_api.h"
[email protected]c10da4b02010-03-25 14:38:3226#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0527#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]14a000d2010-04-29 21:44:2428#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]de768a832009-10-30 05:25:0129#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5630#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2931#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1732#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3433#include "chrome/browser/extensions/external_extension_provider.h"
34#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]56ad3792010-05-28 17:45:3335#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]052313b2010-02-19 09:43:0836#include "chrome/browser/pref_service.h"
[email protected]81e63782009-02-27 19:35:0937#include "chrome/browser/profile.h"
[email protected]56ad3792010-05-28 17:45:3338#include "chrome/browser/search_engines/template_url_model.h"
[email protected]11edd1e2010-07-21 00:14:5039#include "chrome/browser/sync/glue/extension_util.h"
[email protected]aab98a52009-12-02 03:22:3540#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5441#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5842#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4043#include "chrome/common/extensions/extension_constants.h"
[email protected]05c82182010-06-24 17:49:0844#include "chrome/common/extensions/extension_error_utils.h"
[email protected]7c927b62010-02-24 09:54:1345#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0646#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2647#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2948#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3449#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1950#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1451#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3252#include "googleurl/src/gurl.h"
[email protected]24b538a2010-02-27 01:22:4453#include "webkit/database/database_tracker.h"
54#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3155
[email protected]79db6232009-02-13 20:51:2056#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3457#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2058#endif
[email protected]6014d672008-12-05 00:38:2559
[email protected]5ef47ec2010-01-28 05:58:0560using base::Time;
61
[email protected]c6d474f82009-12-16 21:11:0662namespace errors = extension_manifest_errors;
63
[email protected]b6ab96d2009-08-20 18:58:1964namespace {
65
[email protected]2111b1a2010-03-12 18:12:4466static bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
67 // Always reload LOAD extension manifests, because they can change on disk
68 // independent of the manifest in our prefs.
69 if (info.extension_location == Extension::LOAD)
70 return true;
71
72 // Otherwise, reload the manifest it needs to be relocalized.
73 return extension_l10n_util::ShouldRelocalizeManifest(info);
74}
75
[email protected]c6d474f82009-12-16 21:11:0676} // namespace
[email protected]b6ab96d2009-08-20 18:58:1977
[email protected]aa142702010-03-26 01:26:3378PendingExtensionInfo::PendingExtensionInfo(const GURL& update_url,
[email protected]aa142702010-03-26 01:26:3379 bool is_theme,
[email protected]4416c5a2010-06-26 01:28:5780 bool install_silently,
81 bool enable_on_install,
82 bool enable_incognito_on_install)
[email protected]aa142702010-03-26 01:26:3383 : update_url(update_url),
[email protected]aa142702010-03-26 01:26:3384 is_theme(is_theme),
[email protected]4416c5a2010-06-26 01:28:5785 install_silently(install_silently),
86 enable_on_install(enable_on_install),
87 enable_incognito_on_install(enable_incognito_on_install) {}
[email protected]aa142702010-03-26 01:26:3388
89PendingExtensionInfo::PendingExtensionInfo()
90 : update_url(),
[email protected]aa142702010-03-26 01:26:3391 is_theme(false),
[email protected]4416c5a2010-06-26 01:28:5792 install_silently(false),
93 enable_on_install(false),
94 enable_incognito_on_install(false) {}
[email protected]aa142702010-03-26 01:26:3395
[email protected]25b34332009-06-05 21:53:1996// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2597
[email protected]cc655912009-01-29 23:19:1998const char* ExtensionsService::kInstallDirectoryName = "Extensions";
99const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42100
[email protected]334e04a2010-06-24 23:34:44101bool ExtensionsService::IsGalleryDownloadURL(const GURL& download_url) {
[email protected]b7c2f252009-12-08 00:47:23102 if (StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44103 extension_urls::kMiniGalleryDownloadPrefix, false))
104 return true;
[email protected]b7c2f252009-12-08 00:47:23105
[email protected]334e04a2010-06-24 23:34:44106 GURL gallery_download_prefix(extension_urls::kGalleryDownloadPrefix);
107 if (download_url.host() == gallery_download_prefix.host() &&
108 StartsWithASCII(download_url.path(),
109 gallery_download_prefix.path(), false))
[email protected]b7c2f252009-12-08 00:47:23110 return true;
[email protected]b7c2f252009-12-08 00:47:23111
[email protected]473ff6e2010-05-12 15:31:55112 // Allow command line gallery url to be referrer for the gallery downloads.
113 std::string command_line_gallery_url =
114 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
[email protected]2bce5e12010-05-13 01:18:07115 switches::kAppsGalleryURL);
[email protected]473ff6e2010-05-12 15:31:55116 if (!command_line_gallery_url.empty() &&
117 StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44118 extension_urls::kGalleryDownloadPrefix, false))
[email protected]473ff6e2010-05-12 15:31:55119 return true;
[email protected]334e04a2010-06-24 23:34:44120
121 return false;
122}
123
124// static
125bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
126 const GURL& referrer_url) {
127 if (!IsGalleryDownloadURL(download_url))
128 return false;
129
130 if (StartsWithASCII(referrer_url.spec(),
131 extension_urls::kMiniGalleryBrowsePrefix, false))
132 return true;
133
134 if (StartsWithASCII(referrer_url.spec(),
135 Extension::ChromeStoreURL(), false))
136 return true;
137
138 // Allow command line gallery url to be referrer for the gallery downloads.
139 std::string command_line_gallery_url =
140 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
141 switches::kAppsGalleryURL);
142 if (!command_line_gallery_url.empty() &&
143 StartsWithASCII(referrer_url.spec(),
144 command_line_gallery_url, false))
145 return true;
[email protected]473ff6e2010-05-12 15:31:55146
[email protected]b7c2f252009-12-08 00:47:23147 return false;
148}
149
[email protected]ac025282009-12-16 19:16:38150bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
151 return StartsWithASCII(download_url.spec(),
152 extension_urls::kMiniGalleryDownloadPrefix,
153 false); // case_sensitive
154}
155
[email protected]81e63782009-02-27 19:35:09156ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08157 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23158 PrefService* prefs,
159 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17160 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12161 : profile_(profile),
162 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23163 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23164 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13165 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31166 ready_(false),
167 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08168 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23169 if (command_line->HasSwitch(switches::kDisableExtensions)) {
170 extensions_enabled_ = false;
171 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
172 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18173 }
[email protected]36a784c2009-06-23 06:21:08174
[email protected]4814b512009-11-07 00:12:29175 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
176 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16177 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]31f77262009-12-02 20:48:53178 Source<Profile>(profile_));
[email protected]4814b512009-11-07 00:12:29179
[email protected]93fd78f42009-07-10 16:43:17180 // Set up the ExtensionUpdater
181 if (autoupdate_enabled) {
182 int update_frequency = kDefaultUpdateFrequencySeconds;
183 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]e83326f2010-07-31 17:29:25184 base::StringToInt(command_line->GetSwitchValueASCII(
185 switches::kExtensionsUpdateFrequency),
186 &update_frequency);
[email protected]93fd78f42009-07-10 16:43:17187 }
[email protected]95d29192009-10-30 01:49:06188 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17189 }
190
[email protected]95d29192009-10-30 01:49:06191 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]b671760b2010-07-15 21:13:47192
193 // Use monochrome icons for omnibox icons.
194 omnibox_icon_manager_.set_monochrome(true);
[email protected]6014d672008-12-05 00:38:25195}
196
197ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32198 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17199 if (updater_.get()) {
200 updater_->Stop();
201 }
[email protected]6014d672008-12-05 00:38:25202}
203
[email protected]c5ae74ab2010-04-15 18:14:37204void ExtensionsService::InitEventRouters() {
205 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
206 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]56ee0152010-06-16 01:54:42207 ExtensionBrowserEventRouter::GetInstance()->Init(profile_);
[email protected]c5ae74ab2010-04-15 18:14:37208 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
209 profile_->GetBookmarkModel());
[email protected]2c5e1e12010-06-10 13:14:44210 ExtensionCookiesEventRouter::GetInstance()->Init();
[email protected]c5ae74ab2010-04-15 18:14:37211}
212
[email protected]9f1087e2009-06-15 17:29:32213void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29214 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17215 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32216
[email protected]95dd38f2009-10-20 20:09:15217 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
218 // the first extension, because its members listen for loaded notifications.
219 g_browser_process->resource_dispatcher_host();
220
[email protected]9f1087e2009-06-15 17:29:32221 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57222
[email protected]9f1087e2009-06-15 17:29:32223 // TODO(erikkay) this should probably be deferred to a future point
224 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17225 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57226
[email protected]9f1087e2009-06-15 17:29:32227 // TODO(erikkay) this should probably be deferred as well.
228 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25229}
230
[email protected]3cf4f0992009-02-03 23:00:30231void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16232 scoped_refptr<CrxInstaller> installer(
233 new CrxInstaller(install_directory_,
234 this, // frontend
235 NULL)); // no client (silent install)
236 installer->set_allow_privilege_increase(true);
237 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30238}
239
[email protected]aa142702010-03-26 01:26:33240namespace {
241 // TODO(akalin): Put this somewhere where both crx_installer.cc and
242 // this file can use it.
243 void DeleteFileHelper(const FilePath& path, bool recursive) {
244 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
245 file_util::Delete(path, recursive);
246 }
247} // namespace
248
[email protected]e957fe52009-06-23 16:51:05249void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34250 const FilePath& extension_path,
251 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33252 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
253 if ((it == pending_extensions_.end()) &&
254 !GetExtensionByIdInternal(id, true, true)) {
255 LOG(WARNING) << "Will not update extension " << id
256 << " because it is not installed or pending";
257 // Delete extension_path since we're not creating a CrxInstaller
258 // that would do it for us.
259 ChromeThread::PostTask(
260 ChromeThread::FILE, FROM_HERE,
261 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49262 return;
[email protected]e957fe52009-06-23 16:51:05263 }
264
[email protected]aa142702010-03-26 01:26:33265 // We want a silent install only for non-pending extensions and
266 // pending extensions that have install_silently set.
267 ExtensionInstallUI* client =
268 ((it == pending_extensions_.end()) || it->second.install_silently) ?
269 NULL : new ExtensionInstallUI(profile_);
270
[email protected]6dfbbf82010-03-12 23:09:16271 scoped_refptr<CrxInstaller> installer(
272 new CrxInstaller(install_directory_,
273 this, // frontend
[email protected]aa142702010-03-26 01:26:33274 client));
[email protected]6dfbbf82010-03-12 23:09:16275 installer->set_expected_id(id);
276 installer->set_delete_source(true);
[email protected]9f72aa02010-06-25 10:01:05277 installer->set_limit_web_extent_to_download_host(true);
[email protected]5c8516202010-03-18 21:43:34278 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16279 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05280}
281
[email protected]aa142702010-03-26 01:26:33282void ExtensionsService::AddPendingExtension(
283 const std::string& id, const GURL& update_url,
[email protected]4416c5a2010-06-26 01:28:57284 bool is_theme, bool install_silently,
285 bool enable_on_install, bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33286 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25287 LOG(DFATAL) << "Trying to add pending extension " << id
288 << " which already exists";
[email protected]aa142702010-03-26 01:26:33289 return;
290 }
[email protected]4416c5a2010-06-26 01:28:57291 AddPendingExtensionInternal(
292 id, update_url, is_theme, install_silently,
293 enable_on_install, enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33294}
295
296void ExtensionsService::AddPendingExtensionInternal(
297 const std::string& id, const GURL& update_url,
[email protected]4416c5a2010-06-26 01:28:57298 bool is_theme, bool install_silently,
299 bool enable_on_install, bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33300 pending_extensions_[id] =
[email protected]4416c5a2010-06-26 01:28:57301 PendingExtensionInfo(update_url, is_theme, install_silently,
302 enable_on_install, enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33303}
304
[email protected]9cddd4702009-07-27 22:09:40305void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06306 FilePath path;
[email protected]61b411612009-11-10 23:17:41307 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40308
[email protected]b65272f2009-08-31 15:47:06309 // Unload the extension if it's loaded. It might not be loaded if it crashed.
310 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29311 // If the extension has an inspector open for its background page, detach
312 // the inspector and hang onto a cookie for it, so that we can reattach
313 // later.
314 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
315 ExtensionHost* host = manager->GetBackgroundHostForExtension(
316 current_extension);
317 if (host) {
318 // Look for an open inspector for the background page.
319 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
320 host->render_view_host());
321 if (devtools_cookie >= 0)
322 orphaned_dev_tools_[extension_id] = devtools_cookie;
323 }
324
[email protected]b65272f2009-08-31 15:47:06325 path = current_extension->path();
326 UnloadExtension(extension_id);
[email protected]1eb175082010-02-10 09:26:16327 } else {
328 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06329 }
330
[email protected]e6090e42010-03-23 22:44:08331 // Check the installed extensions to see if what we're reloading was already
332 // installed.
333 scoped_ptr<ExtensionInfo> installed_extension(
334 extension_prefs_->GetInstalledExtensionInfo(extension_id));
335 if (installed_extension.get() &&
336 installed_extension->extension_manifest.get()) {
337 LoadInstalledExtension(*installed_extension, false);
338 } else {
339 // We should always be able to remember the extension's path. If it's not in
340 // the map, someone failed to update |unloaded_extension_paths_|.
341 CHECK(!path.empty());
342 LoadExtension(path);
343 }
[email protected]9cddd4702009-07-27 22:09:40344}
345
[email protected]27b985d2009-06-25 17:53:15346void ExtensionsService::UninstallExtension(const std::string& extension_id,
347 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39348 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25349
[email protected]9f1087e2009-06-15 17:29:32350 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51351 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32352
[email protected]831aa212010-03-26 13:55:19353 // Get hold of information we need after unloading, since the extension
354 // pointer will be invalid then.
355 GURL extension_url(extension->url());
356 Extension::Location location(extension->location());
357
358 // Also copy the extension identifier since the reference might have been
359 // obtained via Extension::id().
360 std::string extension_id_copy(extension_id);
361
[email protected]56ad3792010-05-28 17:45:33362 if (profile_->GetTemplateURLModel())
363 profile_->GetTemplateURLModel()->UnregisterExtensionKeyword(extension);
364
[email protected]831aa212010-03-26 13:55:19365 // Unload before doing more cleanup to ensure that nothing is hanging on to
366 // any of these resources.
367 UnloadExtension(extension_id);
368
369 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
370 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32371
372 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19373 if (Extension::LOAD != location) {
[email protected]95d29192009-10-30 01:49:06374 ChromeThread::PostTask(
375 ChromeThread::FILE, FROM_HERE,
376 NewRunnableFunction(
[email protected]ca3dbf52010-05-19 22:27:06377 &extension_file_util::UninstallExtension,
378 install_directory_,
379 extension_id_copy));
[email protected]9f1087e2009-06-15 17:29:32380 }
381
[email protected]c10da4b02010-03-25 14:38:32382 ClearExtensionData(extension_url);
383}
384
385void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
386 scoped_refptr<ExtensionDataDeleter> deleter(
387 new ExtensionDataDeleter(profile_, extension_url));
388 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32389}
390
[email protected]0c6da502009-08-14 22:32:39391void ExtensionsService::EnableExtension(const std::string& extension_id) {
392 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
393 if (!extension) {
394 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
395 return;
396 }
397
[email protected]e8c729a2010-03-09 19:55:19398 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52399
[email protected]0c6da502009-08-14 22:32:39400 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39401 extensions_.push_back(extension);
402 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
403 disabled_extensions_.end(),
404 extension);
405 disabled_extensions_.erase(iter);
406
[email protected]86c008e82009-08-28 20:26:05407 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
408 extension->GetChromeURLOverrides());
409
[email protected]62d30f42009-10-01 22:36:06410 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35411 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39412}
413
[email protected]1784e83a2009-09-08 21:01:52414void ExtensionsService::DisableExtension(const std::string& extension_id) {
415 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15416 // The extension may have been disabled already.
417 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52418 return;
[email protected]1784e83a2009-09-08 21:01:52419
[email protected]e8c729a2010-03-09 19:55:19420 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52421
422 // Move it over to the disabled list.
423 disabled_extensions_.push_back(extension);
424 ExtensionList::iterator iter = std::find(extensions_.begin(),
425 extensions_.end(),
426 extension);
427 extensions_.erase(iter);
428
429 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
430 extension->GetChromeURLOverrides());
431
[email protected]62d30f42009-10-01 22:36:06432 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35433 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52434}
435
[email protected]9f1087e2009-06-15 17:29:32436void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06437 ChromeThread::PostTask(
438 ChromeThread::FILE, FROM_HERE,
439 NewRunnableMethod(
440 backend_.get(),
441 &ExtensionsServiceBackend::LoadSingleExtension,
442 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32443}
444
[email protected]1952c7d2010-03-04 23:48:34445void ExtensionsService::LoadComponentExtensions() {
446 for (RegisteredComponentExtensions::iterator it =
447 component_extension_manifests_.begin();
448 it != component_extension_manifests_.end(); ++it) {
449 JSONStringValueSerializer serializer(it->manifest);
[email protected]ba399672010-04-06 15:42:39450 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
[email protected]999731f2010-03-22 19:13:53451 if (!manifest.get()) {
[email protected]960ef8ae2010-05-06 17:12:29452 DLOG(ERROR) << "Failed to retrieve manifest for extension";
[email protected]999731f2010-03-22 19:13:53453 continue;
454 }
[email protected]1952c7d2010-03-04 23:48:34455
456 scoped_ptr<Extension> extension(new Extension(it->root_directory));
457 extension->set_location(Extension::COMPONENT);
458
459 std::string error;
460 if (!extension->InitFromValue(
461 *static_cast<DictionaryValue*>(manifest.get()),
462 true, // require key
463 &error)) {
[email protected]4fdbc1492010-07-01 01:20:59464 NOTREACHED() << error;
[email protected]1952c7d2010-03-04 23:48:34465 return;
466 }
467
468 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
469 // increase.
470 }
471}
472
[email protected]9f1087e2009-06-15 17:29:32473void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36474 base::TimeTicks start_time = base::TimeTicks::Now();
475
[email protected]1952c7d2010-03-04 23:48:34476 // Load any component extensions.
477 LoadComponentExtensions();
478
[email protected]e72e8eb82009-06-18 17:21:51479 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06480 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08481 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06482
483 // If any extensions need localization, we bounce them all to the file thread
484 // for re-reading and localization.
485 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44486 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]c6d474f82009-12-16 21:11:06487 ChromeThread::PostTask(
488 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
489 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44490 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06491 info.release(), // Callee takes ownership of the memory.
492 start_time,
493 scoped_refptr<ExtensionsService>(this)));
494 return;
495 }
496 }
497
498 // Don't update prefs.
499 // Callee takes ownership of the memory.
500 ContinueLoadAllExtensions(info.release(), start_time, false);
501}
502
503void ExtensionsService::ContinueLoadAllExtensions(
504 ExtensionPrefs::ExtensionsInfo* extensions_info,
505 base::TimeTicks start_time,
506 bool write_to_prefs) {
507 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
508
509 for (size_t i = 0; i < info->size(); ++i) {
510 LoadInstalledExtension(*info->at(i), write_to_prefs);
511 }
512
[email protected]ae09ca62009-08-21 19:46:46513 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36514
515 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
516 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
517
[email protected]1952c7d2010-03-04 23:48:34518 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
519 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36520
[email protected]1952c7d2010-03-04 23:48:34521 int user_script_count = 0;
522 int extension_count = 0;
523 int theme_count = 0;
524 int external_count = 0;
525 int page_action_count = 0;
526 int browser_action_count = 0;
527 ExtensionList::iterator ex;
528 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
529 // Don't count component extensions, since they are only extensions as an
530 // implementation detail.
531 if ((*ex)->location() == Extension::COMPONENT)
532 continue;
533
[email protected]e8c729a2010-03-09 19:55:19534 // Don't count unpacked extensions, since they're a developer-specific
535 // feature.
536 if ((*ex)->location() == Extension::LOAD)
537 continue;
538
[email protected]3ba0fd32010-06-19 05:39:10539 if ((*ex)->is_theme()) {
[email protected]1952c7d2010-03-04 23:48:34540 theme_count++;
541 } else if ((*ex)->converted_from_user_script()) {
542 user_script_count++;
543 } else {
544 extension_count++;
[email protected]cc2c3432009-11-06 17:24:36545 }
[email protected]1952c7d2010-03-04 23:48:34546 if (Extension::IsExternalLocation((*ex)->location())) {
547 external_count++;
548 }
549 if ((*ex)->page_action() != NULL) {
550 page_action_count++;
551 }
552 if ((*ex)->browser_action() != NULL) {
553 browser_action_count++;
554 }
[email protected]cc2c3432009-11-06 17:24:36555 }
[email protected]1952c7d2010-03-04 23:48:34556 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
557 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
558 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
559 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
560 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
561 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
562 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:46563}
564
[email protected]c6d474f82009-12-16 21:11:06565void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
566 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46567 std::string error;
568 Extension* extension = NULL;
[email protected]c6d474f82009-12-16 21:11:06569 if (info.extension_manifest.get()) {
570 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:19571 bool require_key = info.extension_location != Extension::LOAD;
572 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:46573 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46574 } else {
[email protected]c6d474f82009-12-16 21:11:06575 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46576 }
577
578 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06579 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40580 error,
581 NotificationType::EXTENSION_INSTALL_ERROR,
582 false);
[email protected]ae09ca62009-08-21 19:46:46583 return;
584 }
585
[email protected]c6d474f82009-12-16 21:11:06586 extension->set_location(info.extension_location);
587
588 if (write_to_prefs)
589 extension_prefs_->UpdateManifest(extension);
590
[email protected]2a409532009-08-28 19:39:44591 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46592
[email protected]c6d474f82009-12-16 21:11:06593 if (info.extension_location == Extension::EXTERNAL_PREF ||
594 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06595 ChromeThread::PostTask(
596 ChromeThread::FILE, FROM_HERE,
597 NewRunnableMethod(
[email protected]c6d474f82009-12-16 21:11:06598 backend_.get(),
599 &ExtensionsServiceBackend::CheckExternalUninstall,
600 scoped_refptr<ExtensionsService>(this),
601 info.extension_id,
602 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46603 }
[email protected]9f1087e2009-06-15 17:29:32604}
605
[email protected]62d30f42009-10-01 22:36:06606void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
[email protected]57a777f72010-03-31 01:09:42607 // The ChromeURLRequestContexts need to be first to know that the extension
[email protected]62d30f42009-10-01 22:36:06608 // was loaded, otherwise a race can arise where a renderer that is created
609 // for the extension may try to load an extension URL with an extension id
[email protected]57a777f72010-03-31 01:09:42610 // that the request context doesn't yet know about. The profile is responsible
611 // for ensuring its URLRequestContexts appropriately discover the loaded
612 // extension.
613 if (profile_) {
614 profile_->RegisterExtensionWithRequestContexts(extension);
[email protected]24b538a2010-02-27 01:22:44615
616 // Check if this permission requires unlimited storage quota
617 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) {
618 string16 origin_identifier =
619 webkit_database::DatabaseUtil::GetOriginIdentifier(extension->url());
620 ChromeThread::PostTask(
621 ChromeThread::FILE, FROM_HERE,
622 NewRunnableMethod(
623 profile_->GetDatabaseTracker(),
624 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
625 origin_identifier,
626 kint64max));
627 }
[email protected]62d30f42009-10-01 22:36:06628 }
629
[email protected]57a777f72010-03-31 01:09:42630 LOG(INFO) << "Sending EXTENSION_LOADED";
631
[email protected]62d30f42009-10-01 22:36:06632 NotificationService::current()->Notify(
633 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34634 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06635 Details<Extension>(extension));
636}
637
638void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
639 LOG(INFO) << "Sending EXTENSION_UNLOADED";
640
641 NotificationService::current()->Notify(
642 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34643 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06644 Details<Extension>(extension));
645
[email protected]57a777f72010-03-31 01:09:42646 if (profile_) {
647 profile_->UnregisterExtensionWithRequestContexts(extension);
648
649 // Check if this permission required unlimited storage quota, reset its
650 // in-memory quota.
651 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) {
652 string16 origin_identifier =
653 webkit_database::DatabaseUtil::GetOriginIdentifier(extension->url());
[email protected]95d29192009-10-30 01:49:06654 ChromeThread::PostTask(
[email protected]57a777f72010-03-31 01:09:42655 ChromeThread::FILE, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31656 NewRunnableMethod(
[email protected]57a777f72010-03-31 01:09:42657 profile_->GetDatabaseTracker(),
658 &webkit_database::DatabaseTracker::ResetOriginQuotaInMemory,
659 origin_identifier));
[email protected]62d30f42009-10-01 22:36:06660 }
661 }
662}
663
[email protected]6b75ec32009-08-14 06:37:18664void ExtensionsService::UpdateExtensionBlacklist(
665 const std::vector<std::string>& blacklist) {
666 // Use this set to indicate if an extension in the blacklist has been used.
667 std::set<std::string> blacklist_set;
668 for (unsigned int i = 0; i < blacklist.size(); ++i) {
669 if (Extension::IdIsValid(blacklist[i])) {
670 blacklist_set.insert(blacklist[i]);
671 }
672 }
673 extension_prefs_->UpdateBlacklist(blacklist_set);
674 std::vector<std::string> to_be_removed;
675 // Loop current extensions, unload installed extensions.
676 for (ExtensionList::const_iterator iter = extensions_.begin();
677 iter != extensions_.end(); ++iter) {
678 Extension* extension = (*iter);
679 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
680 to_be_removed.push_back(extension->id());
681 }
682 }
683
684 // UnloadExtension will change the extensions_ list. So, we should
685 // call it outside the iterator loop.
686 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
687 UnloadExtension(to_be_removed[i]);
688 }
689}
690
[email protected]cb0ce1e022010-03-10 19:54:41691bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
692 // If this is a component extension we always allow it to work in incognito
693 // mode.
694 if (extension->location() == Extension::COMPONENT)
695 return true;
696
697 // Check the prefs.
698 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:50699}
[email protected]55a35692010-02-11 23:25:21700
[email protected]cb0ce1e022010-03-10 19:54:41701void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:50702 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:41703 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:24704
[email protected]568f33d2010-08-04 17:06:41705 // Broadcast unloaded and loaded events to update browser state. Only bother
706 // if the extension is actually enabled, since there is no UI otherwise.
707 bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
708 extension) != extensions_.end();
709 if (is_enabled) {
710 NotifyExtensionUnloaded(extension);
711 NotifyExtensionLoaded(extension);
712 }
[email protected]55a35692010-02-11 23:25:21713}
714
[email protected]05c82182010-06-24 17:49:08715bool ExtensionsService::AllowFileAccess(const Extension* extension) {
716 return (CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]334e04a2010-06-24 23:34:44717 switches::kDisableExtensionsFileAccessCheck) ||
[email protected]05c82182010-06-24 17:49:08718 extension_prefs_->AllowFileAccess(extension->id()));
719}
720
721void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
722 extension_prefs_->SetAllowFileAccess(extension->id(), allow);
723 NotificationService::current()->Notify(
724 NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
725 Source<Profile>(profile_),
726 Details<Extension>(extension));
727}
728
729bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
730 const GURL& url,
731 std::string* error) const {
732 // No extensions are allowed to execute script on the gallery because that
733 // would allow extensions to manipulate their own install pages.
[email protected]638d45d2010-07-09 17:35:08734 if (url.host() == GURL(Extension::ChromeStoreURL()).host()
735 && !CommandLine::ForCurrentProcess()->HasSwitch(
736 switches::kAllowScriptingGallery)) {
[email protected]05c82182010-06-24 17:49:08737 if (error)
738 *error = errors::kCannotScriptGallery;
739 return false;
740 }
741
742 if (extension->HasHostPermission(url))
743 return true;
744
745 if (error) {
746 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
747 url.spec());
748 }
749
750 return false;
751}
752
[email protected]93fd78f42009-07-10 16:43:17753void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32754 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58755 // TODO(aa): Why pass this list into the provider, why not just filter it
756 // later?
[email protected]9f1087e2009-06-15 17:29:32757 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51758 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06759 ChromeThread::PostTask(
760 ChromeThread::FILE, FROM_HERE,
761 NewRunnableMethod(
762 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
763 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32764}
765
766void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09767 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39768 scoped_ptr<Extension> extension(
769 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25770
[email protected]894bb502009-05-21 22:39:57771 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39772 CHECK(extension.get());
773
[email protected]1eb175082010-02-10 09:26:16774 // Keep information about the extension so that we can reload it later
775 // even if it's not permanently installed.
776 unloaded_extension_paths_[extension->id()] = extension->path();
777
[email protected]86c008e82009-08-28 20:26:05778 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
779 extension->GetChromeURLOverrides());
780
[email protected]0c6da502009-08-14 22:32:39781 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
782 disabled_extensions_.end(),
783 extension.get());
784 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39785 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47786 NotificationService::current()->Notify(
787 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34788 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47789 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39790 return;
791 }
792
793 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57794
[email protected]631cf822009-05-15 07:01:25795 // Remove the extension from our list.
796 extensions_.erase(iter);
797
[email protected]62d30f42009-10-01 22:36:06798 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35799 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25800}
801
[email protected]9f1087e2009-06-15 17:29:32802void ExtensionsService::UnloadAllExtensions() {
[email protected]cd500f72010-06-25 23:44:32803 STLDeleteContainerPointers(extensions_.begin(), extensions_.end());
[email protected]9f1087e2009-06-15 17:29:32804 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29805
[email protected]cd500f72010-06-25 23:44:32806 STLDeleteContainerPointers(disabled_extensions_.begin(),
807 disabled_extensions_.end());
808 disabled_extensions_.clear();
809
[email protected]c6e4a3412009-06-24 15:45:29810 // TODO(erikkay) should there be a notification for this? We can't use
811 // EXTENSION_UNLOADED since that implies that the extension has been disabled
812 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32813}
814
815void ExtensionsService::ReloadExtensions() {
816 UnloadAllExtensions();
817 LoadAllExtensions();
818}
819
820void ExtensionsService::GarbageCollectExtensions() {
[email protected]ba399672010-04-06 15:42:39821 if (extension_prefs_->pref_service()->read_only())
822 return;
823
[email protected]ca3dbf52010-05-19 22:27:06824 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
825 extension_prefs_->GetInstalledExtensionsInfo());
826
827 std::map<std::string, FilePath> extension_paths;
828 for (size_t i = 0; i < info->size(); ++i)
829 extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
830
[email protected]95d29192009-10-30 01:49:06831 ChromeThread::PostTask(
832 ChromeThread::FILE, FROM_HERE,
833 NewRunnableFunction(
834 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]ca3dbf52010-05-19 22:27:06835 extension_paths));
[email protected]3cf4f0992009-02-03 23:00:30836}
837
[email protected]e72e8eb82009-06-18 17:21:51838void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13839 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17840 if (updater_.get()) {
841 updater_->Start();
842 }
[email protected]e72e8eb82009-06-18 17:21:51843 NotificationService::current()->Notify(
844 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34845 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51846 NotificationService::NoDetails());
847}
848
[email protected]6d2e60bd2010-06-03 22:37:39849void ExtensionsService::OnExtensionLoaded(Extension* extension,
[email protected]2a409532009-08-28 19:39:44850 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46851 // Ensure extension is deleted unless we transfer ownership.
852 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32853
[email protected]1eb175082010-02-10 09:26:16854 // The extension is now loaded, remove its data from unloaded extension map.
855 unloaded_extension_paths_.erase(extension->id());
856
[email protected]ceefd3d2010-03-12 09:10:29857 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
858 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:46859 if (extensions_enabled() ||
[email protected]3ba0fd32010-06-19 05:39:10860 extension->is_theme() ||
[email protected]ae09ca62009-08-21 19:46:46861 extension->location() == Extension::LOAD ||
862 Extension::IsExternalLocation(extension->location())) {
863 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
864 if (old) {
[email protected]ca3dbf52010-05-19 22:27:06865 // CrxInstaller should have guaranteed that we aren't downgrading.
866 CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
[email protected]0c6da502009-08-14 22:32:39867
[email protected]ca3dbf52010-05-19 22:27:06868 bool allow_silent_upgrade =
869 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
870 old, extension);
[email protected]1e8c93f2010-02-08 22:58:31871
[email protected]ca3dbf52010-05-19 22:27:06872 // Extensions get upgraded if silent upgrades are allowed, otherwise
873 // they get disabled.
874 if (allow_silent_upgrade) {
875 old->set_being_upgraded(true);
876 extension->set_being_upgraded(true);
877 }
[email protected]0c6da502009-08-14 22:32:39878
[email protected]ca3dbf52010-05-19 22:27:06879 // To upgrade an extension in place, unload the old one and
880 // then load the new one.
881 UnloadExtension(old->id());
882 old = NULL;
883
884 if (!allow_silent_upgrade) {
885 // Extension has changed permissions significantly. Disable it. We
886 // send a notification below.
887 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
888 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]0c6da502009-08-14 22:32:39889 }
[email protected]ba74f352009-06-11 18:54:45890 }
[email protected]86a274072009-06-11 02:06:45891
[email protected]ae09ca62009-08-21 19:46:46892 switch (extension_prefs_->GetExtensionState(extension->id())) {
893 case Extension::ENABLED:
894 extensions_.push_back(scoped_extension.release());
895
[email protected]62d30f42009-10-01 22:36:06896 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46897
[email protected]e8c729a2010-03-09 19:55:19898 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
899 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46900 break;
901 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:45902 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:40903 NotificationService::current()->Notify(
904 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34905 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40906 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46907 break;
908 default:
[email protected]d11c8e92009-10-20 23:26:40909 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46910 break;
[email protected]811f3432009-07-25 19:38:21911 }
[email protected]e72e8eb82009-06-18 17:21:51912 }
[email protected]aab98a52009-12-02 03:22:35913
[email protected]1e8c93f2010-02-08 22:58:31914 extension->set_being_upgraded(false);
915
[email protected]aab98a52009-12-02 03:22:35916 UpdateActiveExtensionsInCrashReporter();
[email protected]0b004da2010-07-02 17:54:31917
918 if (profile_->GetTemplateURLModel())
919 profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension);
[email protected]b671760b2010-07-15 21:13:47920
921 // Load the icon for omnibox-enabled extensions so it will be ready to display
922 // in the URL bar.
923 if (!extension->omnibox_keyword().empty())
924 omnibox_icon_manager_.LoadIcon(extension);
[email protected]aab98a52009-12-02 03:22:35925}
926
927void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:39928 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:35929 for (size_t i = 0; i < extensions_.size(); ++i) {
[email protected]3ba0fd32010-06-19 05:39:10930 if (!extensions_[i]->is_theme())
[email protected]c8865962009-12-16 07:47:39931 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:35932 }
933
934 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:25935}
936
[email protected]2a409532009-08-28 19:39:44937void ExtensionsService::OnExtensionInstalled(Extension* extension,
938 bool allow_privilege_increase) {
[email protected]4416c5a2010-06-26 01:28:57939 // Ensure extension is deleted unless we transfer ownership.
940 scoped_ptr<Extension> scoped_extension(extension);
941 Extension::State initial_state = Extension::DISABLED;
942 bool initial_enable_incognito = false;
[email protected]aa142702010-03-26 01:26:33943 PendingExtensionMap::iterator it =
944 pending_extensions_.find(extension->id());
[email protected]4416c5a2010-06-26 01:28:57945 if (it != pending_extensions_.end()) {
[email protected]11edd1e2010-07-21 00:14:50946 PendingExtensionInfo pending_extension_info = it->second;
947 pending_extensions_.erase(it);
948 it = pending_extensions_.end();
[email protected]4416c5a2010-06-26 01:28:57949 // Set initial state from pending extension data.
[email protected]11edd1e2010-07-21 00:14:50950 if (pending_extension_info.is_theme != extension->is_theme()) {
[email protected]4416c5a2010-06-26 01:28:57951 LOG(WARNING)
952 << "Not installing pending extension " << extension->id()
953 << " with is_theme = " << extension->is_theme()
[email protected]11edd1e2010-07-21 00:14:50954 << "; expected is_theme = " << pending_extension_info.is_theme;
[email protected]4416c5a2010-06-26 01:28:57955 // Delete the extension directory since we're not going to
956 // load it.
957 ChromeThread::PostTask(
958 ChromeThread::FILE, FROM_HERE,
959 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
960 return;
961 }
[email protected]11edd1e2010-07-21 00:14:50962 if (!extension->is_theme() &&
963 !browser_sync::IsExtensionSyncable(*extension)) {
964 // We're an extension installed via sync that is unsyncable,
965 // i.e. we may have been syncable previously. We block these
966 // installs. We'll have to update the clause above if we decide
967 // to sync other extension-like things, like apps or user
968 // scripts.
969 //
970 // Note that this creates a small window where a user who tries
971 // to download/install an extension that is simultaneously
972 // installed via sync (and blocked) will find his download
973 // blocked.
974 //
975 // TODO(akalin): Remove this check once we've put in UI to
976 // approve synced extensions.
977 LOG(WARNING)
978 << "Not installing non-syncable extension " << extension->id();
979 // Delete the extension directory since we're not going to
980 // load it.
981 ChromeThread::PostTask(
982 ChromeThread::FILE, FROM_HERE,
983 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
984 return;
985 }
986 if (pending_extension_info.is_theme) {
987 DCHECK(pending_extension_info.enable_on_install);
[email protected]4416c5a2010-06-26 01:28:57988 initial_state = Extension::ENABLED;
[email protected]11edd1e2010-07-21 00:14:50989 DCHECK(!pending_extension_info.enable_incognito_on_install);
[email protected]4416c5a2010-06-26 01:28:57990 initial_enable_incognito = false;
991 } else {
992 initial_state =
[email protected]11edd1e2010-07-21 00:14:50993 pending_extension_info.enable_on_install ?
[email protected]4416c5a2010-06-26 01:28:57994 Extension::ENABLED : Extension::DISABLED;
995 initial_enable_incognito =
[email protected]11edd1e2010-07-21 00:14:50996 pending_extension_info.enable_incognito_on_install;
[email protected]4416c5a2010-06-26 01:28:57997 }
[email protected]4416c5a2010-06-26 01:28:57998 } else {
[email protected]dbec3792010-08-10 00:08:45999 // Make sure we preserve enabled/disabled states.
[email protected]4416c5a2010-06-26 01:28:571000 Extension::State existing_state =
1001 extension_prefs_->GetExtensionState(extension->id());
1002 initial_state =
1003 (existing_state == Extension::DISABLED) ?
1004 Extension::DISABLED : Extension::ENABLED;
[email protected]dbec3792010-08-10 00:08:451005 initial_enable_incognito =
1006 extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]aa142702010-03-26 01:26:331007 }
1008
[email protected]4416c5a2010-06-26 01:28:571009 extension_prefs_->OnExtensionInstalled(
1010 extension, initial_state, initial_enable_incognito);
[email protected]25b34332009-06-05 21:53:191011
[email protected]92a5b1d2010-07-20 00:42:001012 // Unpacked extensions start off with file access since they are a developer
1013 // feature.
1014 if (extension->location() == Extension::LOAD)
1015 extension_prefs_->SetAllowFileAccess(extension->id(), true);
1016
[email protected]4a190632009-05-09 01:07:421017 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
1018 // to apply it.
[email protected]3ba0fd32010-06-19 05:39:101019 if (extension->is_theme()) {
[email protected]9ceb07342009-07-26 04:09:231020 NotificationService::current()->Notify(
1021 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341022 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:231023 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:271024 } else {
1025 NotificationService::current()->Notify(
1026 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341027 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:271028 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:421029 }
[email protected]7577a5c52009-07-30 06:21:581030
[email protected]4416c5a2010-06-26 01:28:571031 // Transfer ownership of |extension| to OnExtensionLoaded.
1032 OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:421033}
1034
[email protected]0c6da502009-08-14 22:32:391035Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
1036 bool include_enabled,
1037 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:051038 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:391039 if (include_enabled) {
1040 for (ExtensionList::const_iterator iter = extensions_.begin();
1041 iter != extensions_.end(); ++iter) {
1042 if ((*iter)->id() == lowercase_id)
1043 return *iter;
1044 }
1045 }
1046 if (include_disabled) {
1047 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
1048 iter != disabled_extensions_.end(); ++iter) {
1049 if ((*iter)->id() == lowercase_id)
1050 return *iter;
1051 }
[email protected]ce5c4502009-05-06 16:46:111052 }
1053 return NULL;
1054}
1055
[email protected]9f1087e2009-06-15 17:29:321056Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
[email protected]a888b29e62010-04-01 13:38:571057 return url.scheme() != chrome::kExtensionScheme ? NULL :
1058 GetExtensionById(url.host(), false);
1059}
1060
1061Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) {
1062 for (size_t i = 0; i < extensions_.size(); ++i) {
1063 if (extensions_[i]->web_extent().ContainsURL(url))
1064 return extensions_[i];
1065 }
1066 return NULL;
[email protected]9f1087e2009-06-15 17:29:321067}
1068
[email protected]6d2e60bd2010-06-03 22:37:391069Extension* ExtensionsService::GetExtensionByOverlappingWebExtent(
[email protected]9f72aa02010-06-25 10:01:051070 const ExtensionExtent& extent) {
[email protected]22c966c2010-06-26 06:35:021071 for (size_t i = 0; i < extensions_.size(); ++i) {
1072 if (extensions_[i]->web_extent().OverlapsWith(extent))
1073 return extensions_[i];
1074 }
1075
[email protected]6d2e60bd2010-06-03 22:37:391076 return NULL;
1077}
1078
[email protected]b671760b2010-07-15 21:13:471079const SkBitmap& ExtensionsService::GetOmniboxIcon(
1080 const std::string& extension_id) {
1081 return omnibox_icon_manager_.GetIcon(extension_id);
1082}
1083
[email protected]a1257b12009-06-12 02:51:341084void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:061085 ChromeThread::PostTask(
1086 ChromeThread::FILE, FROM_HERE,
1087 NewRunnableMethod(
1088 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:341089}
1090
1091void ExtensionsService::SetProviderForTesting(
1092 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:061093 ChromeThread::PostTask(
1094 ChromeThread::FILE, FROM_HERE,
1095 NewRunnableMethod(
1096 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
1097 location, test_provider));
[email protected]a1257b12009-06-12 02:51:341098}
1099
[email protected]7577a5c52009-07-30 06:21:581100void ExtensionsService::OnExternalExtensionFound(const std::string& id,
1101 const std::string& version,
1102 const FilePath& path,
1103 Extension::Location location) {
1104 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:491105 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:581106 // installed on every startup.
[email protected]61b411612009-11-10 23:17:411107 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:111108 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:581109 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:111110 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:581111 case -1: // existing version is older, we should upgrade
1112 break;
1113 case 0: // existing version is same, do nothing
1114 return;
1115 case 1: // existing version is newer, uh-oh
1116 LOG(WARNING) << "Found external version of extension " << id
1117 << "that is older than current version. Current version "
1118 << "is: " << existing->VersionString() << ". New version "
1119 << "is: " << version << ". Keeping current version.";
1120 return;
1121 }
1122 }
1123
[email protected]6dfbbf82010-03-12 23:09:161124 scoped_refptr<CrxInstaller> installer(
1125 new CrxInstaller(install_directory_,
1126 this, // frontend
1127 NULL)); // no client (silent install)
1128 installer->set_install_source(location);
1129 installer->set_expected_id(id);
1130 installer->set_allow_privilege_increase(true);
1131 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581132}
1133
[email protected]d11c8e92009-10-20 23:26:401134void ExtensionsService::ReportExtensionLoadError(
1135 const FilePath& extension_path,
1136 const std::string &error,
1137 NotificationType type,
1138 bool be_noisy) {
1139 NotificationService* service = NotificationService::current();
1140 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341141 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401142 Details<const std::string>(&error));
1143
1144 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161145 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:401146 std::string message = StringPrintf("Could not load extension from '%s'. %s",
1147 path_str.c_str(), error.c_str());
1148 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1149}
1150
[email protected]4814b512009-11-07 00:12:291151void ExtensionsService::Observe(NotificationType type,
1152 const NotificationSource& source,
1153 const NotificationDetails& details) {
1154 switch (type.value) {
1155 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
1156 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1157 OrphanedDevTools::iterator iter =
1158 orphaned_dev_tools_.find(host->extension()->id());
1159 if (iter == orphaned_dev_tools_.end())
1160 return;
1161
1162 DevToolsManager::GetInstance()->AttachClientHost(
1163 iter->second, host->render_view_host());
1164 orphaned_dev_tools_.erase(iter);
1165 break;
1166 }
1167
[email protected]a4ed6282009-12-14 20:51:161168 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:531169 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:161170
[email protected]f128af42010-08-05 18:05:261171 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1172
1173 // TODO(rafaelw): Remove this check and ExtensionHost::recently_deleted().
1174 // This is only here to help track down crbug.com/49114.
1175 ExtensionHost::HostPointerList::iterator iter =
1176 ExtensionHost::recently_deleted()->begin();
1177 for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
1178 if (*iter == host) {
1179 CHECK(host->GetURL().spec().size() + 2 != 0);
1180 break;
1181 }
1182 }
1183 if (iter == ExtensionHost::recently_deleted()->end())
1184 CHECK(host->GetURL().spec().size() + 1 != 0);
1185
[email protected]31f77262009-12-02 20:48:531186 // Unload the entire extension. We want it to be in a consistent state:
1187 // either fully working or not loaded at all, but never half-crashed.
[email protected]f128af42010-08-05 18:05:261188 UnloadExtension(host->extension()->id());
[email protected]31f77262009-12-02 20:48:531189 break;
1190 }
1191
[email protected]4814b512009-11-07 00:12:291192 default:
1193 NOTREACHED() << "Unexpected notification type.";
1194 }
1195}
1196
[email protected]377011d2010-07-20 04:18:501197bool ExtensionsService::HasApps() {
1198 if (!extensions_enabled_)
1199 return false;
1200
1201 for (ExtensionList::const_iterator it = extensions_.begin();
1202 it != extensions_.end(); ++it) {
1203 if ((*it)->is_app())
1204 return true;
1205 }
1206
1207 return false;
1208}
[email protected]4814b512009-11-07 00:12:291209
[email protected]6014d672008-12-05 00:38:251210// ExtensionsServicesBackend
1211
[email protected]894bb502009-05-21 22:39:571212ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:061213 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:081214 : frontend_(NULL),
1215 install_directory_(install_directory),
[email protected]95d29192009-10-30 01:49:061216 alert_on_error_(false) {
[email protected]7577a5c52009-07-30 06:21:581217 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
1218 // pref data in the ctor and that is called on the UI thread. Would be better
1219 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:341220 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:011221 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:151222 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341223#if defined(OS_WIN)
1224 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:011225 linked_ptr<ExternalExtensionProvider>(
1226 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341227#endif
1228}
1229
1230ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:571231}
1232
[email protected]b0beaa662009-02-26 00:04:151233void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:571234 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151235 frontend_ = frontend;
1236
1237 // Explicit UI loads are always noisy.
1238 alert_on_error_ = true;
1239
[email protected]cc5da332009-03-04 08:02:511240 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:501241 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:451242
1243 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:161244 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:451245
[email protected]ab6f2b22009-07-28 23:28:371246 std::string error;
1247 Extension* extension = extension_file_util::LoadExtension(
1248 extension_path,
1249 false, // Don't require id
1250 &error);
1251
1252 if (!extension) {
1253 ReportExtensionLoadError(extension_path, error);
1254 return;
[email protected]0877fd92009-02-03 16:34:061255 }
[email protected]ab6f2b22009-07-28 23:28:371256
1257 extension->set_location(Extension::LOAD);
[email protected]e8c729a2010-03-09 19:55:191258
1259 // Report this as an installed extension so that it gets remembered in the
1260 // prefs.
1261 ChromeThread::PostTask(
1262 ChromeThread::UI, FROM_HERE,
1263 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
1264 extension, true));
[email protected]0877fd92009-02-03 16:34:061265}
1266
[email protected]6014d672008-12-05 00:38:251267void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:511268 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:061269 ChromeThread::PostTask(
1270 ChromeThread::UI, FROM_HERE,
1271 NewRunnableMethod(
1272 frontend_,
[email protected]d11c8e92009-10-20 23:26:401273 &ExtensionsService::ReportExtensionLoadError, extension_path,
1274 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:251275}
1276
[email protected]a1257b12009-06-12 02:51:341277bool ExtensionsServiceBackend::LookupExternalExtension(
1278 const std::string& id, Version** version, Extension::Location* location) {
1279 scoped_ptr<Version> extension_version;
1280 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1281 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011282 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341283 extension_version.reset(provider->RegisteredVersion(id, location));
1284 if (extension_version.get()) {
1285 if (version)
1286 *version = extension_version.release();
1287 return true;
1288 }
1289 }
1290 return false;
1291}
1292
[email protected]b0beaa662009-02-26 00:04:151293// Some extensions will autoupdate themselves externally from Chrome. These
1294// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191295// these, the extension will register its location in the the preferences file
1296// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151297// check that location for a .crx file, which it will then install locally if
1298// a new version is available.
1299void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571300 std::set<std::string> ids_to_ignore,
1301 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151302 // Note that this installation is intentionally silent (since it didn't
1303 // go through the front-end). Extensions that are registered in this
1304 // way are effectively considered 'pre-bundled', and so implicitly
1305 // trusted. In general, if something has HKLM or filesystem access,
1306 // they could install an extension manually themselves anyway.
1307 alert_on_error_ = false;
1308 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151309
[email protected]a1257b12009-06-12 02:51:341310 // Ask each external extension provider to give us a call back for each
1311 // extension they know about. See OnExternalExtensionFound.
1312 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1313 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011314 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341315 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191316 }
[email protected]b0beaa662009-02-26 00:04:151317}
1318
[email protected]ae09ca62009-08-21 19:46:461319void ExtensionsServiceBackend::CheckExternalUninstall(
1320 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1321 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341322 // Check if the providers know about this extension.
1323 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461324 if (i == external_extension_providers_.end()) {
1325 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1326 << location;
1327 return;
[email protected]b0beaa662009-02-26 00:04:151328 }
[email protected]25b34332009-06-05 21:53:191329
[email protected]ae09ca62009-08-21 19:46:461330 scoped_ptr<Version> version;
1331 version.reset(i->second->RegisteredVersion(id, NULL));
1332 if (version.get())
1333 return; // Yup, known extension, don't uninstall.
1334
1335 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061336 ChromeThread::PostTask(
1337 ChromeThread::UI, FROM_HERE,
1338 NewRunnableMethod(
1339 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151340}
1341
[email protected]a1257b12009-06-12 02:51:341342void ExtensionsServiceBackend::ClearProvidersForTesting() {
1343 external_extension_providers_.clear();
1344}
1345
1346void ExtensionsServiceBackend::SetProviderForTesting(
1347 Extension::Location location,
1348 ExternalExtensionProvider* test_provider) {
1349 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011350 external_extension_providers_[location] =
1351 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341352}
1353
1354void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:581355 const std::string& id, const Version* version, const FilePath& path,
1356 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061357 ChromeThread::PostTask(
1358 ChromeThread::UI, FROM_HERE,
1359 NewRunnableMethod(
1360 frontend_, &ExtensionsService::OnExternalExtensionFound, id,
1361 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191362}
[email protected]c6d474f82009-12-16 21:11:061363
[email protected]2111b1a2010-03-12 18:12:441364void ExtensionsServiceBackend::ReloadExtensionManifests(
[email protected]c6d474f82009-12-16 21:11:061365 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1366 base::TimeTicks start_time,
1367 scoped_refptr<ExtensionsService> frontend) {
1368 frontend_ = frontend;
1369
1370 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1371 ExtensionInfo* info = extensions_to_reload->at(i).get();
[email protected]2111b1a2010-03-12 18:12:441372 if (!ShouldReloadExtensionManifest(*info))
[email protected]c6d474f82009-12-16 21:11:061373 continue;
1374
1375 // We need to reload original manifest in order to localize properly.
1376 std::string error;
1377 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1378 info->extension_path, false, &error));
1379
1380 if (extension.get())
1381 extensions_to_reload->at(i)->extension_manifest.reset(
1382 static_cast<DictionaryValue*>(
1383 extension->manifest_value()->DeepCopy()));
1384 }
1385
1386 // Finish installing on UI thread.
1387 ChromeThread::PostTask(
1388 ChromeThread::UI, FROM_HERE,
1389 NewRunnableMethod(
1390 frontend_,
1391 &ExtensionsService::ContinueLoadAllExtensions,
1392 extensions_to_reload,
1393 start_time,
1394 true));
1395}