blob: 58ead19db8df37386ba917958d57ae4db0eb45e3 [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]ce7f62e32010-08-10 23:43:5916#include "base/utf_string_conversions.h"
[email protected]cc655912009-01-29 23:19:1917#include "base/values.h"
[email protected]aa142702010-03-26 01:26:3318#include "base/version.h"
[email protected]15730c42009-09-03 00:03:2019#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2120#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2921#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5822#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5623#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3824#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2825#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]2c5e1e12010-06-10 13:14:4426#include "chrome/browser/extensions/extension_cookies_api.h"
[email protected]c10da4b02010-03-25 14:38:3227#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0528#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]14a000d2010-04-29 21:44:2429#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]de768a832009-10-30 05:25:0130#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5631#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2932#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1733#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3434#include "chrome/browser/extensions/external_extension_provider.h"
35#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]56ad3792010-05-28 17:45:3336#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]052313b2010-02-19 09:43:0837#include "chrome/browser/pref_service.h"
[email protected]81e63782009-02-27 19:35:0938#include "chrome/browser/profile.h"
[email protected]56ad3792010-05-28 17:45:3339#include "chrome/browser/search_engines/template_url_model.h"
[email protected]2a3e22b12010-08-13 04:55:1740#include "chrome/browser/sync/glue/extension_sync_traits.h"
[email protected]11edd1e2010-07-21 00:14:5041#include "chrome/browser/sync/glue/extension_util.h"
[email protected]aab98a52009-12-02 03:22:3542#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5443#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5844#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4045#include "chrome/common/extensions/extension_constants.h"
[email protected]05c82182010-06-24 17:49:0846#include "chrome/common/extensions/extension_error_utils.h"
[email protected]7c927b62010-02-24 09:54:1347#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0648#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2649#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2950#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3451#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1952#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1453#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3254#include "googleurl/src/gurl.h"
[email protected]24b538a2010-02-27 01:22:4455#include "webkit/database/database_tracker.h"
56#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3157
[email protected]79db6232009-02-13 20:51:2058#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3459#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2060#endif
[email protected]6014d672008-12-05 00:38:2561
[email protected]5ef47ec2010-01-28 05:58:0562using base::Time;
63
[email protected]c6d474f82009-12-16 21:11:0664namespace errors = extension_manifest_errors;
65
[email protected]b6ab96d2009-08-20 18:58:1966namespace {
67
[email protected]c2c263c2010-08-13 21:59:4868bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
[email protected]2111b1a2010-03-12 18:12:4469 // Always reload LOAD extension manifests, because they can change on disk
70 // independent of the manifest in our prefs.
71 if (info.extension_location == Extension::LOAD)
72 return true;
73
74 // Otherwise, reload the manifest it needs to be relocalized.
75 return extension_l10n_util::ShouldRelocalizeManifest(info);
76}
77
[email protected]c2c263c2010-08-13 21:59:4878void GetExplicitOriginsInExtent(Extension* extension,
79 std::vector<GURL>* origins) {
80 typedef std::vector<URLPattern> PatternList;
81 std::set<GURL> set;
82 const PatternList& patterns = extension->web_extent().patterns();
83 for (PatternList::const_iterator pattern = patterns.begin();
84 pattern != patterns.end(); ++pattern) {
85 if (pattern->match_subdomains() || pattern->match_all_urls())
86 continue;
87 GURL origin = GURL(pattern->GetAsString()).GetOrigin();
88 if (origin.is_valid())
89 set.insert(origin);
90 }
91
92 for (std::set<GURL>::const_iterator unique = set.begin();
93 unique != set.end(); ++unique) {
94 origins->push_back(*unique);
95 }
96}
97
[email protected]c6d474f82009-12-16 21:11:0698} // namespace
[email protected]b6ab96d2009-08-20 18:58:1999
[email protected]8ef78fd2010-08-19 17:14:32100PendingExtensionInfo::PendingExtensionInfo(
101 const GURL& update_url,
102 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
103 bool is_from_sync,
104 bool install_silently,
105 bool enable_on_install,
106 bool enable_incognito_on_install)
[email protected]aa142702010-03-26 01:26:33107 : update_url(update_url),
[email protected]8ef78fd2010-08-19 17:14:32108 expected_crx_type(expected_crx_type),
109 is_from_sync(is_from_sync),
[email protected]4416c5a2010-06-26 01:28:57110 install_silently(install_silently),
111 enable_on_install(enable_on_install),
112 enable_incognito_on_install(enable_incognito_on_install) {}
[email protected]aa142702010-03-26 01:26:33113
114PendingExtensionInfo::PendingExtensionInfo()
115 : update_url(),
[email protected]8ef78fd2010-08-19 17:14:32116 expected_crx_type(PendingExtensionInfo::UNKNOWN),
117 is_from_sync(true),
[email protected]4416c5a2010-06-26 01:28:57118 install_silently(false),
119 enable_on_install(false),
120 enable_incognito_on_install(false) {}
[email protected]aa142702010-03-26 01:26:33121
[email protected]25b34332009-06-05 21:53:19122// ExtensionsService.
[email protected]6014d672008-12-05 00:38:25123
[email protected]cc655912009-01-29 23:19:19124const char* ExtensionsService::kInstallDirectoryName = "Extensions";
125const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42126
[email protected]06f37542010-08-14 18:26:28127namespace {
128
129bool IsGalleryDownloadURL(const GURL& download_url) {
[email protected]b7c2f252009-12-08 00:47:23130 if (StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44131 extension_urls::kMiniGalleryDownloadPrefix, false))
132 return true;
[email protected]b7c2f252009-12-08 00:47:23133
[email protected]06f37542010-08-14 18:26:28134 if (StartsWithASCII(download_url.spec(),
135 extension_urls::kGalleryDownloadPrefix, false))
136 return true;
[email protected]b7c2f252009-12-08 00:47:23137
[email protected]473ff6e2010-05-12 15:31:55138 // Allow command line gallery url to be referrer for the gallery downloads.
139 std::string command_line_gallery_url =
140 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
[email protected]2bce5e12010-05-13 01:18:07141 switches::kAppsGalleryURL);
[email protected]473ff6e2010-05-12 15:31:55142 if (!command_line_gallery_url.empty() &&
143 StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44144 extension_urls::kGalleryDownloadPrefix, false))
[email protected]473ff6e2010-05-12 15:31:55145 return true;
[email protected]334e04a2010-06-24 23:34:44146
147 return false;
148}
149
[email protected]06f37542010-08-14 18:26:28150} // namespace
151
[email protected]334e04a2010-06-24 23:34:44152// static
153bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
154 const GURL& referrer_url) {
155 if (!IsGalleryDownloadURL(download_url))
156 return false;
157
158 if (StartsWithASCII(referrer_url.spec(),
159 extension_urls::kMiniGalleryBrowsePrefix, false))
160 return true;
161
162 if (StartsWithASCII(referrer_url.spec(),
163 Extension::ChromeStoreURL(), false))
164 return true;
165
166 // Allow command line gallery url to be referrer for the gallery downloads.
167 std::string command_line_gallery_url =
168 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
169 switches::kAppsGalleryURL);
170 if (!command_line_gallery_url.empty() &&
171 StartsWithASCII(referrer_url.spec(),
172 command_line_gallery_url, false))
173 return true;
[email protected]473ff6e2010-05-12 15:31:55174
[email protected]b7c2f252009-12-08 00:47:23175 return false;
176}
177
[email protected]ac025282009-12-16 19:16:38178bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
179 return StartsWithASCII(download_url.spec(),
180 extension_urls::kMiniGalleryDownloadPrefix,
181 false); // case_sensitive
182}
183
[email protected]81e63782009-02-27 19:35:09184ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08185 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23186 PrefService* prefs,
187 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17188 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12189 : profile_(profile),
190 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23191 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23192 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13193 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31194 ready_(false),
195 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08196 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23197 if (command_line->HasSwitch(switches::kDisableExtensions)) {
198 extensions_enabled_ = false;
199 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
200 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18201 }
[email protected]36a784c2009-06-23 06:21:08202
[email protected]4814b512009-11-07 00:12:29203 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
204 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16205 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]31f77262009-12-02 20:48:53206 Source<Profile>(profile_));
[email protected]aa96d3a2010-08-21 08:45:25207 prefs->AddPrefObserver(prefs::kExtensionInstallAllowList, this);
208 prefs->AddPrefObserver(prefs::kExtensionInstallDenyList, this);
[email protected]4814b512009-11-07 00:12:29209
[email protected]93fd78f42009-07-10 16:43:17210 // Set up the ExtensionUpdater
211 if (autoupdate_enabled) {
212 int update_frequency = kDefaultUpdateFrequencySeconds;
213 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]e83326f2010-07-31 17:29:25214 base::StringToInt(command_line->GetSwitchValueASCII(
215 switches::kExtensionsUpdateFrequency),
216 &update_frequency);
[email protected]93fd78f42009-07-10 16:43:17217 }
[email protected]95d29192009-10-30 01:49:06218 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17219 }
220
[email protected]95d29192009-10-30 01:49:06221 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]b671760b2010-07-15 21:13:47222
[email protected]aa96d3a2010-08-21 08:45:25223 // Use monochrome icons for Omnibox icons.
[email protected]b671760b2010-07-15 21:13:47224 omnibox_icon_manager_.set_monochrome(true);
[email protected]6014d672008-12-05 00:38:25225}
226
227ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32228 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17229 if (updater_.get()) {
230 updater_->Stop();
231 }
[email protected]6014d672008-12-05 00:38:25232}
233
[email protected]c5ae74ab2010-04-15 18:14:37234void ExtensionsService::InitEventRouters() {
235 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
236 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]56ee0152010-06-16 01:54:42237 ExtensionBrowserEventRouter::GetInstance()->Init(profile_);
[email protected]c5ae74ab2010-04-15 18:14:37238 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
239 profile_->GetBookmarkModel());
[email protected]2c5e1e12010-06-10 13:14:44240 ExtensionCookiesEventRouter::GetInstance()->Init();
[email protected]c5ae74ab2010-04-15 18:14:37241}
242
[email protected]9f1087e2009-06-15 17:29:32243void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29244 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17245 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32246
[email protected]95dd38f2009-10-20 20:09:15247 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
248 // the first extension, because its members listen for loaded notifications.
249 g_browser_process->resource_dispatcher_host();
250
[email protected]9f1087e2009-06-15 17:29:32251 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57252
[email protected]9f1087e2009-06-15 17:29:32253 // TODO(erikkay) this should probably be deferred to a future point
254 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17255 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57256
[email protected]9f1087e2009-06-15 17:29:32257 // TODO(erikkay) this should probably be deferred as well.
258 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25259}
260
[email protected]3cf4f0992009-02-03 23:00:30261void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16262 scoped_refptr<CrxInstaller> installer(
263 new CrxInstaller(install_directory_,
264 this, // frontend
265 NULL)); // no client (silent install)
266 installer->set_allow_privilege_increase(true);
267 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30268}
269
[email protected]aa142702010-03-26 01:26:33270namespace {
271 // TODO(akalin): Put this somewhere where both crx_installer.cc and
272 // this file can use it.
273 void DeleteFileHelper(const FilePath& path, bool recursive) {
274 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
275 file_util::Delete(path, recursive);
276 }
277} // namespace
278
[email protected]e957fe52009-06-23 16:51:05279void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34280 const FilePath& extension_path,
281 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33282 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
[email protected]8ef78fd2010-08-19 17:14:32283 bool is_pending_extension = (it != pending_extensions_.end());
284
285 if (!is_pending_extension &&
[email protected]aa142702010-03-26 01:26:33286 !GetExtensionByIdInternal(id, true, true)) {
287 LOG(WARNING) << "Will not update extension " << id
288 << " because it is not installed or pending";
289 // Delete extension_path since we're not creating a CrxInstaller
290 // that would do it for us.
291 ChromeThread::PostTask(
292 ChromeThread::FILE, FROM_HERE,
293 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49294 return;
[email protected]e957fe52009-06-23 16:51:05295 }
296
[email protected]aa142702010-03-26 01:26:33297 // We want a silent install only for non-pending extensions and
298 // pending extensions that have install_silently set.
299 ExtensionInstallUI* client =
[email protected]8ef78fd2010-08-19 17:14:32300 (!is_pending_extension || it->second.install_silently) ?
[email protected]aa142702010-03-26 01:26:33301 NULL : new ExtensionInstallUI(profile_);
302
[email protected]6dfbbf82010-03-12 23:09:16303 scoped_refptr<CrxInstaller> installer(
304 new CrxInstaller(install_directory_,
305 this, // frontend
[email protected]aa142702010-03-26 01:26:33306 client));
[email protected]6dfbbf82010-03-12 23:09:16307 installer->set_expected_id(id);
[email protected]8ef78fd2010-08-19 17:14:32308 if (is_pending_extension && !it->second.is_from_sync)
309 installer->set_install_source(Extension::EXTERNAL_PREF_DOWNLOAD);
[email protected]6dfbbf82010-03-12 23:09:16310 installer->set_delete_source(true);
[email protected]5c8516202010-03-18 21:43:34311 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16312 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05313}
314
[email protected]8ef78fd2010-08-19 17:14:32315void ExtensionsService::AddPendingExtensionFromSync(
[email protected]aa142702010-03-26 01:26:33316 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32317 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
318 bool install_silently, bool enable_on_install,
319 bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33320 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25321 LOG(DFATAL) << "Trying to add pending extension " << id
322 << " which already exists";
[email protected]aa142702010-03-26 01:26:33323 return;
324 }
[email protected]4416c5a2010-06-26 01:28:57325 AddPendingExtensionInternal(
[email protected]8ef78fd2010-08-19 17:14:32326 id, update_url, expected_crx_type, true, install_silently,
[email protected]4416c5a2010-06-26 01:28:57327 enable_on_install, enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33328}
329
[email protected]8ef78fd2010-08-19 17:14:32330void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl(
331 const std::string& id, const GURL& update_url) {
332 // Add the extension to this list of extensions to update.
333 // We do not know if the id refers to a theme, so make is_theme unknown.
334 const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType =
335 PendingExtensionInfo::UNKNOWN;
336 const bool kIsFromSync = false;
337 const bool kInstallSilently = true;
338 const bool kEnableOnInstall = true;
339 const bool kEnableIncognitoOnInstall = false;
340
341 if (GetExtensionByIdInternal(id, true, true)) {
342 LOG(DFATAL) << "Trying to add extension " << id
343 << " by external update, but it is already installed.";
344 return;
345 }
346
347 AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync,
348 kInstallSilently, kEnableOnInstall,
349 kEnableIncognitoOnInstall);
350}
351
[email protected]aa142702010-03-26 01:26:33352void ExtensionsService::AddPendingExtensionInternal(
353 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32354 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
355 bool is_from_sync, bool install_silently,
[email protected]4416c5a2010-06-26 01:28:57356 bool enable_on_install, bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33357 pending_extensions_[id] =
[email protected]8ef78fd2010-08-19 17:14:32358 PendingExtensionInfo(update_url, expected_crx_type, is_from_sync,
359 install_silently, enable_on_install,
360 enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33361}
362
[email protected]9cddd4702009-07-27 22:09:40363void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06364 FilePath path;
[email protected]61b411612009-11-10 23:17:41365 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40366
[email protected]f17dbd42010-08-16 23:21:10367 // Disable the extension if it's loaded. It might not be loaded if it crashed.
[email protected]b65272f2009-08-31 15:47:06368 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29369 // If the extension has an inspector open for its background page, detach
370 // the inspector and hang onto a cookie for it, so that we can reattach
371 // later.
372 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
373 ExtensionHost* host = manager->GetBackgroundHostForExtension(
374 current_extension);
375 if (host) {
376 // Look for an open inspector for the background page.
377 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
378 host->render_view_host());
379 if (devtools_cookie >= 0)
380 orphaned_dev_tools_[extension_id] = devtools_cookie;
381 }
382
[email protected]b65272f2009-08-31 15:47:06383 path = current_extension->path();
[email protected]f17dbd42010-08-16 23:21:10384 DisableExtension(extension_id);
385 disabled_extension_paths_[extension_id] = path;
[email protected]1eb175082010-02-10 09:26:16386 } else {
387 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06388 }
389
[email protected]e6090e42010-03-23 22:44:08390 // Check the installed extensions to see if what we're reloading was already
391 // installed.
392 scoped_ptr<ExtensionInfo> installed_extension(
393 extension_prefs_->GetInstalledExtensionInfo(extension_id));
394 if (installed_extension.get() &&
395 installed_extension->extension_manifest.get()) {
396 LoadInstalledExtension(*installed_extension, false);
397 } else {
398 // We should always be able to remember the extension's path. If it's not in
399 // the map, someone failed to update |unloaded_extension_paths_|.
400 CHECK(!path.empty());
401 LoadExtension(path);
402 }
[email protected]9cddd4702009-07-27 22:09:40403}
404
[email protected]27b985d2009-06-25 17:53:15405void ExtensionsService::UninstallExtension(const std::string& extension_id,
406 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39407 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25408
[email protected]9f1087e2009-06-15 17:29:32409 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51410 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32411
[email protected]4c793f02010-08-18 20:55:45412 // Notify interested parties that we're uninstalling this extension.
413 NotificationService::current()->Notify(
414 NotificationType::EXTENSION_UNINSTALLED,
415 Source<Profile>(profile_),
416 Details<Extension>(extension));
417
[email protected]831aa212010-03-26 13:55:19418 // Get hold of information we need after unloading, since the extension
419 // pointer will be invalid then.
420 GURL extension_url(extension->url());
421 Extension::Location location(extension->location());
422
423 // Also copy the extension identifier since the reference might have been
424 // obtained via Extension::id().
425 std::string extension_id_copy(extension_id);
426
[email protected]56ad3792010-05-28 17:45:33427 if (profile_->GetTemplateURLModel())
428 profile_->GetTemplateURLModel()->UnregisterExtensionKeyword(extension);
429
[email protected]831aa212010-03-26 13:55:19430 // Unload before doing more cleanup to ensure that nothing is hanging on to
431 // any of these resources.
432 UnloadExtension(extension_id);
433
434 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
435 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32436
437 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19438 if (Extension::LOAD != location) {
[email protected]95d29192009-10-30 01:49:06439 ChromeThread::PostTask(
440 ChromeThread::FILE, FROM_HERE,
441 NewRunnableFunction(
[email protected]ca3dbf52010-05-19 22:27:06442 &extension_file_util::UninstallExtension,
443 install_directory_,
444 extension_id_copy));
[email protected]9f1087e2009-06-15 17:29:32445 }
446
[email protected]c10da4b02010-03-25 14:38:32447 ClearExtensionData(extension_url);
448}
449
450void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
451 scoped_refptr<ExtensionDataDeleter> deleter(
452 new ExtensionDataDeleter(profile_, extension_url));
453 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32454}
455
[email protected]0c6da502009-08-14 22:32:39456void ExtensionsService::EnableExtension(const std::string& extension_id) {
457 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
458 if (!extension) {
459 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
460 return;
461 }
462
[email protected]e8c729a2010-03-09 19:55:19463 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52464
[email protected]0c6da502009-08-14 22:32:39465 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39466 extensions_.push_back(extension);
467 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
468 disabled_extensions_.end(),
469 extension);
470 disabled_extensions_.erase(iter);
471
[email protected]86c008e82009-08-28 20:26:05472 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
473 extension->GetChromeURLOverrides());
474
[email protected]62d30f42009-10-01 22:36:06475 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35476 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39477}
478
[email protected]1784e83a2009-09-08 21:01:52479void ExtensionsService::DisableExtension(const std::string& extension_id) {
480 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15481 // The extension may have been disabled already.
482 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52483 return;
[email protected]1784e83a2009-09-08 21:01:52484
[email protected]e8c729a2010-03-09 19:55:19485 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52486
487 // Move it over to the disabled list.
488 disabled_extensions_.push_back(extension);
489 ExtensionList::iterator iter = std::find(extensions_.begin(),
490 extensions_.end(),
491 extension);
492 extensions_.erase(iter);
493
494 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
495 extension->GetChromeURLOverrides());
496
[email protected]62d30f42009-10-01 22:36:06497 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35498 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52499}
500
[email protected]9f1087e2009-06-15 17:29:32501void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06502 ChromeThread::PostTask(
503 ChromeThread::FILE, FROM_HERE,
504 NewRunnableMethod(
505 backend_.get(),
506 &ExtensionsServiceBackend::LoadSingleExtension,
507 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32508}
509
[email protected]1952c7d2010-03-04 23:48:34510void ExtensionsService::LoadComponentExtensions() {
511 for (RegisteredComponentExtensions::iterator it =
512 component_extension_manifests_.begin();
513 it != component_extension_manifests_.end(); ++it) {
514 JSONStringValueSerializer serializer(it->manifest);
[email protected]ba399672010-04-06 15:42:39515 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
[email protected]999731f2010-03-22 19:13:53516 if (!manifest.get()) {
[email protected]960ef8ae2010-05-06 17:12:29517 DLOG(ERROR) << "Failed to retrieve manifest for extension";
[email protected]999731f2010-03-22 19:13:53518 continue;
519 }
[email protected]1952c7d2010-03-04 23:48:34520
521 scoped_ptr<Extension> extension(new Extension(it->root_directory));
522 extension->set_location(Extension::COMPONENT);
523
524 std::string error;
525 if (!extension->InitFromValue(
526 *static_cast<DictionaryValue*>(manifest.get()),
527 true, // require key
528 &error)) {
[email protected]4fdbc1492010-07-01 01:20:59529 NOTREACHED() << error;
[email protected]1952c7d2010-03-04 23:48:34530 return;
531 }
532
533 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
534 // increase.
535 }
536}
537
[email protected]9f1087e2009-06-15 17:29:32538void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36539 base::TimeTicks start_time = base::TimeTicks::Now();
540
[email protected]1952c7d2010-03-04 23:48:34541 // Load any component extensions.
542 LoadComponentExtensions();
543
[email protected]e72e8eb82009-06-18 17:21:51544 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06545 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08546 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06547
548 // If any extensions need localization, we bounce them all to the file thread
549 // for re-reading and localization.
550 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44551 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]c6d474f82009-12-16 21:11:06552 ChromeThread::PostTask(
553 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
554 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44555 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06556 info.release(), // Callee takes ownership of the memory.
557 start_time,
558 scoped_refptr<ExtensionsService>(this)));
559 return;
560 }
561 }
562
563 // Don't update prefs.
564 // Callee takes ownership of the memory.
565 ContinueLoadAllExtensions(info.release(), start_time, false);
566}
567
568void ExtensionsService::ContinueLoadAllExtensions(
569 ExtensionPrefs::ExtensionsInfo* extensions_info,
570 base::TimeTicks start_time,
571 bool write_to_prefs) {
572 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
573
574 for (size_t i = 0; i < info->size(); ++i) {
575 LoadInstalledExtension(*info->at(i), write_to_prefs);
576 }
577
[email protected]ae09ca62009-08-21 19:46:46578 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36579
580 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
581 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
582
[email protected]1952c7d2010-03-04 23:48:34583 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
584 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36585
[email protected]1952c7d2010-03-04 23:48:34586 int user_script_count = 0;
587 int extension_count = 0;
588 int theme_count = 0;
589 int external_count = 0;
590 int page_action_count = 0;
591 int browser_action_count = 0;
592 ExtensionList::iterator ex;
593 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
594 // Don't count component extensions, since they are only extensions as an
595 // implementation detail.
596 if ((*ex)->location() == Extension::COMPONENT)
597 continue;
598
[email protected]e8c729a2010-03-09 19:55:19599 // Don't count unpacked extensions, since they're a developer-specific
600 // feature.
601 if ((*ex)->location() == Extension::LOAD)
602 continue;
603
[email protected]3ba0fd32010-06-19 05:39:10604 if ((*ex)->is_theme()) {
[email protected]1952c7d2010-03-04 23:48:34605 theme_count++;
606 } else if ((*ex)->converted_from_user_script()) {
607 user_script_count++;
608 } else {
609 extension_count++;
[email protected]cc2c3432009-11-06 17:24:36610 }
[email protected]1952c7d2010-03-04 23:48:34611 if (Extension::IsExternalLocation((*ex)->location())) {
612 external_count++;
613 }
614 if ((*ex)->page_action() != NULL) {
615 page_action_count++;
616 }
617 if ((*ex)->browser_action() != NULL) {
618 browser_action_count++;
619 }
[email protected]cc2c3432009-11-06 17:24:36620 }
[email protected]1952c7d2010-03-04 23:48:34621 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
622 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
623 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
624 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
625 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
626 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
627 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:46628}
629
[email protected]c6d474f82009-12-16 21:11:06630void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
631 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46632 std::string error;
633 Extension* extension = NULL;
[email protected]306a2bd2010-08-11 14:56:36634 if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) {
635 error = errors::kDisabledByPolicy;
636 } else if (info.extension_manifest.get()) {
[email protected]c6d474f82009-12-16 21:11:06637 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:19638 bool require_key = info.extension_location != Extension::LOAD;
639 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:46640 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46641 } else {
[email protected]c6d474f82009-12-16 21:11:06642 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46643 }
644
645 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06646 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40647 error,
648 NotificationType::EXTENSION_INSTALL_ERROR,
649 false);
[email protected]ae09ca62009-08-21 19:46:46650 return;
651 }
652
[email protected]c6d474f82009-12-16 21:11:06653 extension->set_location(info.extension_location);
654
655 if (write_to_prefs)
656 extension_prefs_->UpdateManifest(extension);
657
[email protected]2a409532009-08-28 19:39:44658 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46659
[email protected]c6d474f82009-12-16 21:11:06660 if (info.extension_location == Extension::EXTERNAL_PREF ||
661 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06662 ChromeThread::PostTask(
663 ChromeThread::FILE, FROM_HERE,
664 NewRunnableMethod(
[email protected]f17dbd42010-08-16 23:21:10665 backend_.get(),
666 &ExtensionsServiceBackend::CheckExternalUninstall,
667 scoped_refptr<ExtensionsService>(this),
668 info.extension_id,
669 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46670 }
[email protected]9f1087e2009-06-15 17:29:32671}
672
[email protected]62d30f42009-10-01 22:36:06673void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
[email protected]57a777f72010-03-31 01:09:42674 // The ChromeURLRequestContexts need to be first to know that the extension
[email protected]62d30f42009-10-01 22:36:06675 // was loaded, otherwise a race can arise where a renderer that is created
676 // for the extension may try to load an extension URL with an extension id
[email protected]57a777f72010-03-31 01:09:42677 // that the request context doesn't yet know about. The profile is responsible
678 // for ensuring its URLRequestContexts appropriately discover the loaded
679 // extension.
680 if (profile_) {
681 profile_->RegisterExtensionWithRequestContexts(extension);
[email protected]24b538a2010-02-27 01:22:44682
683 // Check if this permission requires unlimited storage quota
[email protected]c2c263c2010-08-13 21:59:48684 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
685 GrantUnlimitedStorage(extension);
[email protected]62d30f42009-10-01 22:36:06686 }
687
[email protected]57a777f72010-03-31 01:09:42688 LOG(INFO) << "Sending EXTENSION_LOADED";
689
[email protected]62d30f42009-10-01 22:36:06690 NotificationService::current()->Notify(
691 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34692 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06693 Details<Extension>(extension));
694}
695
696void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
697 LOG(INFO) << "Sending EXTENSION_UNLOADED";
698
699 NotificationService::current()->Notify(
700 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34701 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06702 Details<Extension>(extension));
703
[email protected]57a777f72010-03-31 01:09:42704 if (profile_) {
705 profile_->UnregisterExtensionWithRequestContexts(extension);
706
707 // Check if this permission required unlimited storage quota, reset its
708 // in-memory quota.
[email protected]c2c263c2010-08-13 21:59:48709 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
710 RevokeUnlimitedStorage(extension);
711 }
712}
713
714void ExtensionsService::GrantUnlimitedStorage(Extension* extension) {
715 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
716 std::vector<GURL> origins;
717 GetExplicitOriginsInExtent(extension, &origins);
718 origins.push_back(extension->url());
719
720 for (size_t i = 0; i < origins.size(); ++i) {
721 const GURL& origin = origins[i];
722 if (++unlimited_storage_map_[origin] == 1) {
[email protected]57a777f72010-03-31 01:09:42723 string16 origin_identifier =
[email protected]c2c263c2010-08-13 21:59:48724 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
725 ChromeThread::PostTask(
726 ChromeThread::FILE, FROM_HERE,
727 NewRunnableMethod(
728 profile_->GetDatabaseTracker(),
729 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
730 origin_identifier,
731 kint64max));
732 ChromeThread::PostTask(
733 ChromeThread::IO, FROM_HERE,
734 NewRunnableMethod(
735 profile_->GetAppCacheService(),
736 &ChromeAppCacheService::SetOriginQuotaInMemory,
737 origin,
738 kint64max));
739 }
740 }
741}
742
743void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) {
744 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
745 std::vector<GURL> origins;
746 GetExplicitOriginsInExtent(extension, &origins);
747 origins.push_back(extension->url());
748
749 for (size_t i = 0; i < origins.size(); ++i) {
750 const GURL& origin = origins[i];
751 DCHECK(unlimited_storage_map_[origin] > 0);
752 if (--unlimited_storage_map_[origin] == 0) {
753 unlimited_storage_map_.erase(origin);
754 string16 origin_identifier =
755 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
[email protected]95d29192009-10-30 01:49:06756 ChromeThread::PostTask(
[email protected]57a777f72010-03-31 01:09:42757 ChromeThread::FILE, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31758 NewRunnableMethod(
[email protected]57a777f72010-03-31 01:09:42759 profile_->GetDatabaseTracker(),
760 &webkit_database::DatabaseTracker::ResetOriginQuotaInMemory,
761 origin_identifier));
[email protected]c2c263c2010-08-13 21:59:48762 ChromeThread::PostTask(
763 ChromeThread::IO, FROM_HERE,
764 NewRunnableMethod(
765 profile_->GetAppCacheService(),
766 &ChromeAppCacheService::ResetOriginQuotaInMemory,
767 origin));
[email protected]62d30f42009-10-01 22:36:06768 }
769 }
770}
771
[email protected]6b75ec32009-08-14 06:37:18772void ExtensionsService::UpdateExtensionBlacklist(
773 const std::vector<std::string>& blacklist) {
774 // Use this set to indicate if an extension in the blacklist has been used.
775 std::set<std::string> blacklist_set;
776 for (unsigned int i = 0; i < blacklist.size(); ++i) {
777 if (Extension::IdIsValid(blacklist[i])) {
778 blacklist_set.insert(blacklist[i]);
779 }
780 }
781 extension_prefs_->UpdateBlacklist(blacklist_set);
782 std::vector<std::string> to_be_removed;
783 // Loop current extensions, unload installed extensions.
784 for (ExtensionList::const_iterator iter = extensions_.begin();
785 iter != extensions_.end(); ++iter) {
786 Extension* extension = (*iter);
787 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
788 to_be_removed.push_back(extension->id());
789 }
790 }
791
792 // UnloadExtension will change the extensions_ list. So, we should
793 // call it outside the iterator loop.
794 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
795 UnloadExtension(to_be_removed[i]);
796 }
797}
798
[email protected]aa96d3a2010-08-21 08:45:25799void ExtensionsService::DestroyingProfile() {
800 profile_->GetPrefs()->RemovePrefObserver(
801 prefs::kExtensionInstallAllowList, this);
802 profile_->GetPrefs()->RemovePrefObserver(
803 prefs::kExtensionInstallDenyList, this);
804
805 profile_ = NULL;
806}
807
808void ExtensionsService::CheckAdminBlacklist() {
809 std::vector<std::string> to_be_removed;
810 // Loop through extensions list, unload installed extensions.
811 for (ExtensionList::const_iterator iter = extensions_.begin();
812 iter != extensions_.end(); ++iter) {
813 Extension* extension = (*iter);
814 if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id()))
815 to_be_removed.push_back(extension->id());
816 }
817
818 // UnloadExtension will change the extensions_ list. So, we should
819 // call it outside the iterator loop.
820 for (unsigned int i = 0; i < to_be_removed.size(); ++i)
821 UnloadExtension(to_be_removed[i]);
822}
823
[email protected]cb0ce1e022010-03-10 19:54:41824bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
825 // If this is a component extension we always allow it to work in incognito
826 // mode.
827 if (extension->location() == Extension::COMPONENT)
828 return true;
829
830 // Check the prefs.
831 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:50832}
[email protected]55a35692010-02-11 23:25:21833
[email protected]cb0ce1e022010-03-10 19:54:41834void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:50835 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:41836 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:24837
[email protected]568f33d2010-08-04 17:06:41838 // Broadcast unloaded and loaded events to update browser state. Only bother
839 // if the extension is actually enabled, since there is no UI otherwise.
840 bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
841 extension) != extensions_.end();
842 if (is_enabled) {
843 NotifyExtensionUnloaded(extension);
844 NotifyExtensionLoaded(extension);
845 }
[email protected]55a35692010-02-11 23:25:21846}
847
[email protected]05c82182010-06-24 17:49:08848bool ExtensionsService::AllowFileAccess(const Extension* extension) {
849 return (CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]334e04a2010-06-24 23:34:44850 switches::kDisableExtensionsFileAccessCheck) ||
[email protected]05c82182010-06-24 17:49:08851 extension_prefs_->AllowFileAccess(extension->id()));
852}
853
854void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
855 extension_prefs_->SetAllowFileAccess(extension->id(), allow);
856 NotificationService::current()->Notify(
857 NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
858 Source<Profile>(profile_),
859 Details<Extension>(extension));
860}
861
862bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
863 const GURL& url,
864 std::string* error) const {
865 // No extensions are allowed to execute script on the gallery because that
866 // would allow extensions to manipulate their own install pages.
[email protected]638d45d2010-07-09 17:35:08867 if (url.host() == GURL(Extension::ChromeStoreURL()).host()
868 && !CommandLine::ForCurrentProcess()->HasSwitch(
869 switches::kAllowScriptingGallery)) {
[email protected]05c82182010-06-24 17:49:08870 if (error)
871 *error = errors::kCannotScriptGallery;
872 return false;
873 }
874
875 if (extension->HasHostPermission(url))
876 return true;
877
878 if (error) {
879 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
880 url.spec());
881 }
882
883 return false;
884}
885
[email protected]93fd78f42009-07-10 16:43:17886void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32887 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58888 // TODO(aa): Why pass this list into the provider, why not just filter it
889 // later?
[email protected]9f1087e2009-06-15 17:29:32890 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51891 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06892 ChromeThread::PostTask(
893 ChromeThread::FILE, FROM_HERE,
894 NewRunnableMethod(
895 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
896 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32897}
898
899void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09900 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39901 scoped_ptr<Extension> extension(
902 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25903
[email protected]894bb502009-05-21 22:39:57904 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39905 CHECK(extension.get());
906
[email protected]1eb175082010-02-10 09:26:16907 // Keep information about the extension so that we can reload it later
908 // even if it's not permanently installed.
909 unloaded_extension_paths_[extension->id()] = extension->path();
910
[email protected]f17dbd42010-08-16 23:21:10911 // Clean up if the extension is meant to be enabled after a reload.
912 disabled_extension_paths_.erase(extension->id());
913
[email protected]86c008e82009-08-28 20:26:05914 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
915 extension->GetChromeURLOverrides());
916
[email protected]0c6da502009-08-14 22:32:39917 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
918 disabled_extensions_.end(),
919 extension.get());
920 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39921 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47922 NotificationService::current()->Notify(
923 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34924 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47925 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39926 return;
927 }
928
929 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57930
[email protected]631cf822009-05-15 07:01:25931 // Remove the extension from our list.
932 extensions_.erase(iter);
933
[email protected]62d30f42009-10-01 22:36:06934 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35935 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25936}
937
[email protected]9f1087e2009-06-15 17:29:32938void ExtensionsService::UnloadAllExtensions() {
[email protected]cd500f72010-06-25 23:44:32939 STLDeleteContainerPointers(extensions_.begin(), extensions_.end());
[email protected]9f1087e2009-06-15 17:29:32940 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29941
[email protected]cd500f72010-06-25 23:44:32942 STLDeleteContainerPointers(disabled_extensions_.begin(),
943 disabled_extensions_.end());
944 disabled_extensions_.clear();
945
[email protected]c6e4a3412009-06-24 15:45:29946 // TODO(erikkay) should there be a notification for this? We can't use
947 // EXTENSION_UNLOADED since that implies that the extension has been disabled
948 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32949}
950
951void ExtensionsService::ReloadExtensions() {
952 UnloadAllExtensions();
953 LoadAllExtensions();
954}
955
956void ExtensionsService::GarbageCollectExtensions() {
[email protected]ba399672010-04-06 15:42:39957 if (extension_prefs_->pref_service()->read_only())
958 return;
959
[email protected]ca3dbf52010-05-19 22:27:06960 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
961 extension_prefs_->GetInstalledExtensionsInfo());
962
963 std::map<std::string, FilePath> extension_paths;
964 for (size_t i = 0; i < info->size(); ++i)
965 extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
966
[email protected]95d29192009-10-30 01:49:06967 ChromeThread::PostTask(
968 ChromeThread::FILE, FROM_HERE,
969 NewRunnableFunction(
970 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]ca3dbf52010-05-19 22:27:06971 extension_paths));
[email protected]3cf4f0992009-02-03 23:00:30972}
973
[email protected]e72e8eb82009-06-18 17:21:51974void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13975 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17976 if (updater_.get()) {
977 updater_->Start();
978 }
[email protected]e72e8eb82009-06-18 17:21:51979 NotificationService::current()->Notify(
980 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34981 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51982 NotificationService::NoDetails());
983}
984
[email protected]6d2e60bd2010-06-03 22:37:39985void ExtensionsService::OnExtensionLoaded(Extension* extension,
[email protected]2a409532009-08-28 19:39:44986 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46987 // Ensure extension is deleted unless we transfer ownership.
988 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32989
[email protected]1eb175082010-02-10 09:26:16990 // The extension is now loaded, remove its data from unloaded extension map.
991 unloaded_extension_paths_.erase(extension->id());
992
[email protected]f17dbd42010-08-16 23:21:10993 // If the extension was disabled for a reload, then enable it.
994 if (disabled_extension_paths_.erase(extension->id()) > 0)
995 EnableExtension(extension->id());
996
[email protected]ceefd3d2010-03-12 09:10:29997 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
998 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:46999 if (extensions_enabled() ||
[email protected]3ba0fd32010-06-19 05:39:101000 extension->is_theme() ||
[email protected]ae09ca62009-08-21 19:46:461001 extension->location() == Extension::LOAD ||
1002 Extension::IsExternalLocation(extension->location())) {
1003 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
1004 if (old) {
[email protected]ca3dbf52010-05-19 22:27:061005 // CrxInstaller should have guaranteed that we aren't downgrading.
1006 CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
[email protected]0c6da502009-08-14 22:32:391007
[email protected]ca3dbf52010-05-19 22:27:061008 bool allow_silent_upgrade =
1009 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
1010 old, extension);
[email protected]1e8c93f2010-02-08 22:58:311011
[email protected]ca3dbf52010-05-19 22:27:061012 // Extensions get upgraded if silent upgrades are allowed, otherwise
1013 // they get disabled.
1014 if (allow_silent_upgrade) {
1015 old->set_being_upgraded(true);
1016 extension->set_being_upgraded(true);
1017 }
[email protected]0c6da502009-08-14 22:32:391018
[email protected]ca3dbf52010-05-19 22:27:061019 // To upgrade an extension in place, unload the old one and
1020 // then load the new one.
1021 UnloadExtension(old->id());
1022 old = NULL;
1023
1024 if (!allow_silent_upgrade) {
1025 // Extension has changed permissions significantly. Disable it. We
1026 // send a notification below.
1027 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
1028 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]0c6da502009-08-14 22:32:391029 }
[email protected]ba74f352009-06-11 18:54:451030 }
[email protected]86a274072009-06-11 02:06:451031
[email protected]ae09ca62009-08-21 19:46:461032 switch (extension_prefs_->GetExtensionState(extension->id())) {
1033 case Extension::ENABLED:
1034 extensions_.push_back(scoped_extension.release());
1035
[email protected]62d30f42009-10-01 22:36:061036 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:461037
[email protected]e8c729a2010-03-09 19:55:191038 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
1039 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:461040 break;
1041 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:451042 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:401043 NotificationService::current()->Notify(
1044 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341045 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401046 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:461047 break;
1048 default:
[email protected]d11c8e92009-10-20 23:26:401049 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:461050 break;
[email protected]811f3432009-07-25 19:38:211051 }
[email protected]e72e8eb82009-06-18 17:21:511052 }
[email protected]aab98a52009-12-02 03:22:351053
[email protected]1e8c93f2010-02-08 22:58:311054 extension->set_being_upgraded(false);
1055
[email protected]aab98a52009-12-02 03:22:351056 UpdateActiveExtensionsInCrashReporter();
[email protected]0b004da2010-07-02 17:54:311057
1058 if (profile_->GetTemplateURLModel())
1059 profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension);
[email protected]b671760b2010-07-15 21:13:471060
1061 // Load the icon for omnibox-enabled extensions so it will be ready to display
1062 // in the URL bar.
1063 if (!extension->omnibox_keyword().empty())
1064 omnibox_icon_manager_.LoadIcon(extension);
[email protected]aab98a52009-12-02 03:22:351065}
1066
1067void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:391068 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:351069 for (size_t i = 0; i < extensions_.size(); ++i) {
[email protected]3ba0fd32010-06-19 05:39:101070 if (!extensions_[i]->is_theme())
[email protected]c8865962009-12-16 07:47:391071 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:351072 }
1073
1074 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:251075}
1076
[email protected]2a409532009-08-28 19:39:441077void ExtensionsService::OnExtensionInstalled(Extension* extension,
1078 bool allow_privilege_increase) {
[email protected]4416c5a2010-06-26 01:28:571079 // Ensure extension is deleted unless we transfer ownership.
1080 scoped_ptr<Extension> scoped_extension(extension);
1081 Extension::State initial_state = Extension::DISABLED;
1082 bool initial_enable_incognito = false;
[email protected]aa142702010-03-26 01:26:331083 PendingExtensionMap::iterator it =
1084 pending_extensions_.find(extension->id());
[email protected]4416c5a2010-06-26 01:28:571085 if (it != pending_extensions_.end()) {
[email protected]11edd1e2010-07-21 00:14:501086 PendingExtensionInfo pending_extension_info = it->second;
[email protected]8ef78fd2010-08-19 17:14:321087 PendingExtensionInfo::ExpectedCrxType expected_crx_type =
1088 pending_extension_info.expected_crx_type;
1089 bool is_from_sync = pending_extension_info.is_from_sync;
[email protected]11edd1e2010-07-21 00:14:501090 pending_extensions_.erase(it);
1091 it = pending_extensions_.end();
[email protected]8ef78fd2010-08-19 17:14:321092
[email protected]4416c5a2010-06-26 01:28:571093 // Set initial state from pending extension data.
[email protected]8ef78fd2010-08-19 17:14:321094 PendingExtensionInfo::ExpectedCrxType actual_crx_type =
1095 (extension->is_theme() ? PendingExtensionInfo::THEME
1096 : PendingExtensionInfo::EXTENSION);
1097
1098 if (expected_crx_type != PendingExtensionInfo::UNKNOWN &&
1099 expected_crx_type != actual_crx_type) {
[email protected]4416c5a2010-06-26 01:28:571100 LOG(WARNING)
1101 << "Not installing pending extension " << extension->id()
[email protected]8ef78fd2010-08-19 17:14:321102 << " with is_theme = " << extension->is_theme();
[email protected]4416c5a2010-06-26 01:28:571103 // Delete the extension directory since we're not going to
1104 // load it.
1105 ChromeThread::PostTask(
1106 ChromeThread::FILE, FROM_HERE,
1107 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1108 return;
1109 }
[email protected]8ef78fd2010-08-19 17:14:321110
1111 // If |extension| is not syncable, and was installed via sync, disallow
1112 // the instanation.
1113 //
1114 // Themes are always allowed. Because they contain no active code, they
1115 // are less of a risk than extensions.
1116 //
1117 // If |is_from_sync| is false, then the install was not initiated by sync,
1118 // and this check should pass. Extensions that were installed from an
1119 // update URL in external_extensions.json are an example. They are not
1120 // syncable, because the user did not make an explicit choice to install
1121 // them. However, they were installed through the update mechanism, so
1122 // control must pass into this function.
1123 //
1124 // TODO(akalin): When we do apps sync, we have to work with its
1125 // traits, too.
[email protected]2a3e22b12010-08-13 04:55:171126 const browser_sync::ExtensionSyncTraits extension_sync_traits =
1127 browser_sync::GetExtensionSyncTraits();
[email protected]06e33202010-08-16 23:45:151128 const browser_sync::ExtensionSyncTraits app_sync_traits =
1129 browser_sync::GetAppSyncTraits();
[email protected]2a3e22b12010-08-13 04:55:171130 // If an extension is a theme, we bypass the valid/syncable check
1131 // as themes are harmless.
[email protected]8ef78fd2010-08-19 17:14:321132 if (!extension->is_theme() && is_from_sync &&
[email protected]2a3e22b12010-08-13 04:55:171133 !browser_sync::IsExtensionValidAndSyncable(
[email protected]06e33202010-08-16 23:45:151134 *extension, extension_sync_traits.allowed_extension_types) &&
1135 !browser_sync::IsExtensionValidAndSyncable(
1136 *extension, app_sync_traits.allowed_extension_types)) {
[email protected]11edd1e2010-07-21 00:14:501137 // We're an extension installed via sync that is unsyncable,
1138 // i.e. we may have been syncable previously. We block these
1139 // installs. We'll have to update the clause above if we decide
1140 // to sync other extension-like things, like apps or user
1141 // scripts.
1142 //
1143 // Note that this creates a small window where a user who tries
1144 // to download/install an extension that is simultaneously
1145 // installed via sync (and blocked) will find his download
1146 // blocked.
1147 //
1148 // TODO(akalin): Remove this check once we've put in UI to
1149 // approve synced extensions.
1150 LOG(WARNING)
[email protected]2a3e22b12010-08-13 04:55:171151 << "Not installing invalid or unsyncable extension "
1152 << extension->id();
[email protected]11edd1e2010-07-21 00:14:501153 // Delete the extension directory since we're not going to
1154 // load it.
1155 ChromeThread::PostTask(
1156 ChromeThread::FILE, FROM_HERE,
1157 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1158 return;
1159 }
[email protected]8ef78fd2010-08-19 17:14:321160 if (extension->is_theme()) {
[email protected]11edd1e2010-07-21 00:14:501161 DCHECK(pending_extension_info.enable_on_install);
[email protected]4416c5a2010-06-26 01:28:571162 initial_state = Extension::ENABLED;
[email protected]11edd1e2010-07-21 00:14:501163 DCHECK(!pending_extension_info.enable_incognito_on_install);
[email protected]4416c5a2010-06-26 01:28:571164 initial_enable_incognito = false;
1165 } else {
1166 initial_state =
[email protected]11edd1e2010-07-21 00:14:501167 pending_extension_info.enable_on_install ?
[email protected]4416c5a2010-06-26 01:28:571168 Extension::ENABLED : Extension::DISABLED;
1169 initial_enable_incognito =
[email protected]11edd1e2010-07-21 00:14:501170 pending_extension_info.enable_incognito_on_install;
[email protected]4416c5a2010-06-26 01:28:571171 }
[email protected]4416c5a2010-06-26 01:28:571172 } else {
[email protected]dbec3792010-08-10 00:08:451173 // Make sure we preserve enabled/disabled states.
[email protected]4416c5a2010-06-26 01:28:571174 Extension::State existing_state =
1175 extension_prefs_->GetExtensionState(extension->id());
1176 initial_state =
1177 (existing_state == Extension::DISABLED) ?
1178 Extension::DISABLED : Extension::ENABLED;
[email protected]dbec3792010-08-10 00:08:451179 initial_enable_incognito =
1180 extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]aa142702010-03-26 01:26:331181 }
1182
[email protected]4416c5a2010-06-26 01:28:571183 extension_prefs_->OnExtensionInstalled(
1184 extension, initial_state, initial_enable_incognito);
[email protected]25b34332009-06-05 21:53:191185
[email protected]92a5b1d2010-07-20 00:42:001186 // Unpacked extensions start off with file access since they are a developer
1187 // feature.
1188 if (extension->location() == Extension::LOAD)
1189 extension_prefs_->SetAllowFileAccess(extension->id(), true);
1190
[email protected]4a190632009-05-09 01:07:421191 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
1192 // to apply it.
[email protected]3ba0fd32010-06-19 05:39:101193 if (extension->is_theme()) {
[email protected]9ceb07342009-07-26 04:09:231194 NotificationService::current()->Notify(
1195 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341196 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:231197 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:271198 } else {
1199 NotificationService::current()->Notify(
1200 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341201 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:271202 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:421203 }
[email protected]7577a5c52009-07-30 06:21:581204
[email protected]4416c5a2010-06-26 01:28:571205 // Transfer ownership of |extension| to OnExtensionLoaded.
1206 OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:421207}
1208
[email protected]0c6da502009-08-14 22:32:391209Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
1210 bool include_enabled,
1211 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:051212 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:391213 if (include_enabled) {
1214 for (ExtensionList::const_iterator iter = extensions_.begin();
1215 iter != extensions_.end(); ++iter) {
1216 if ((*iter)->id() == lowercase_id)
1217 return *iter;
1218 }
1219 }
1220 if (include_disabled) {
1221 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
1222 iter != disabled_extensions_.end(); ++iter) {
1223 if ((*iter)->id() == lowercase_id)
1224 return *iter;
1225 }
[email protected]ce5c4502009-05-06 16:46:111226 }
1227 return NULL;
1228}
1229
[email protected]9f1087e2009-06-15 17:29:321230Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
[email protected]a888b29e62010-04-01 13:38:571231 return url.scheme() != chrome::kExtensionScheme ? NULL :
1232 GetExtensionById(url.host(), false);
1233}
1234
1235Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) {
1236 for (size_t i = 0; i < extensions_.size(); ++i) {
1237 if (extensions_[i]->web_extent().ContainsURL(url))
1238 return extensions_[i];
1239 }
1240 return NULL;
[email protected]9f1087e2009-06-15 17:29:321241}
1242
[email protected]6d2e60bd2010-06-03 22:37:391243Extension* ExtensionsService::GetExtensionByOverlappingWebExtent(
[email protected]9f72aa02010-06-25 10:01:051244 const ExtensionExtent& extent) {
[email protected]22c966c2010-06-26 06:35:021245 for (size_t i = 0; i < extensions_.size(); ++i) {
1246 if (extensions_[i]->web_extent().OverlapsWith(extent))
1247 return extensions_[i];
1248 }
1249
[email protected]6d2e60bd2010-06-03 22:37:391250 return NULL;
1251}
1252
[email protected]b671760b2010-07-15 21:13:471253const SkBitmap& ExtensionsService::GetOmniboxIcon(
1254 const std::string& extension_id) {
1255 return omnibox_icon_manager_.GetIcon(extension_id);
1256}
1257
[email protected]a1257b12009-06-12 02:51:341258void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:061259 ChromeThread::PostTask(
1260 ChromeThread::FILE, FROM_HERE,
1261 NewRunnableMethod(
1262 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:341263}
1264
1265void ExtensionsService::SetProviderForTesting(
1266 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:061267 ChromeThread::PostTask(
1268 ChromeThread::FILE, FROM_HERE,
1269 NewRunnableMethod(
1270 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
1271 location, test_provider));
[email protected]a1257b12009-06-12 02:51:341272}
1273
[email protected]8ef78fd2010-08-19 17:14:321274void ExtensionsService::OnExternalExtensionFileFound(
1275 const std::string& id,
1276 const std::string& version,
1277 const FilePath& path,
1278 Extension::Location location) {
[email protected]7577a5c52009-07-30 06:21:581279 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:491280 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:581281 // installed on every startup.
[email protected]61b411612009-11-10 23:17:411282 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:111283 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:581284 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:111285 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:581286 case -1: // existing version is older, we should upgrade
1287 break;
1288 case 0: // existing version is same, do nothing
1289 return;
1290 case 1: // existing version is newer, uh-oh
1291 LOG(WARNING) << "Found external version of extension " << id
1292 << "that is older than current version. Current version "
1293 << "is: " << existing->VersionString() << ". New version "
1294 << "is: " << version << ". Keeping current version.";
1295 return;
1296 }
1297 }
1298
[email protected]6dfbbf82010-03-12 23:09:161299 scoped_refptr<CrxInstaller> installer(
1300 new CrxInstaller(install_directory_,
1301 this, // frontend
1302 NULL)); // no client (silent install)
1303 installer->set_install_source(location);
1304 installer->set_expected_id(id);
1305 installer->set_allow_privilege_increase(true);
1306 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581307}
1308
[email protected]d11c8e92009-10-20 23:26:401309void ExtensionsService::ReportExtensionLoadError(
1310 const FilePath& extension_path,
1311 const std::string &error,
1312 NotificationType type,
1313 bool be_noisy) {
1314 NotificationService* service = NotificationService::current();
1315 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341316 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401317 Details<const std::string>(&error));
1318
1319 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161320 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:401321 std::string message = StringPrintf("Could not load extension from '%s'. %s",
1322 path_str.c_str(), error.c_str());
1323 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1324}
1325
[email protected]4814b512009-11-07 00:12:291326void ExtensionsService::Observe(NotificationType type,
1327 const NotificationSource& source,
1328 const NotificationDetails& details) {
1329 switch (type.value) {
1330 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
1331 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1332 OrphanedDevTools::iterator iter =
1333 orphaned_dev_tools_.find(host->extension()->id());
1334 if (iter == orphaned_dev_tools_.end())
1335 return;
1336
1337 DevToolsManager::GetInstance()->AttachClientHost(
1338 iter->second, host->render_view_host());
1339 orphaned_dev_tools_.erase(iter);
1340 break;
1341 }
1342
[email protected]a4ed6282009-12-14 20:51:161343 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:531344 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:161345
[email protected]f128af42010-08-05 18:05:261346 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1347
1348 // TODO(rafaelw): Remove this check and ExtensionHost::recently_deleted().
1349 // This is only here to help track down crbug.com/49114.
1350 ExtensionHost::HostPointerList::iterator iter =
1351 ExtensionHost::recently_deleted()->begin();
1352 for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
1353 if (*iter == host) {
1354 CHECK(host->GetURL().spec().size() + 2 != 0);
1355 break;
1356 }
1357 }
1358 if (iter == ExtensionHost::recently_deleted()->end())
1359 CHECK(host->GetURL().spec().size() + 1 != 0);
1360
[email protected]31f77262009-12-02 20:48:531361 // Unload the entire extension. We want it to be in a consistent state:
1362 // either fully working or not loaded at all, but never half-crashed.
[email protected]f128af42010-08-05 18:05:261363 UnloadExtension(host->extension()->id());
[email protected]31f77262009-12-02 20:48:531364 break;
1365 }
1366
[email protected]aa96d3a2010-08-21 08:45:251367 case NotificationType::PREF_CHANGED: {
1368 std::string* pref_name = Details<std::string>(details).ptr();
1369 DCHECK(*pref_name == prefs::kExtensionInstallAllowList ||
1370 *pref_name == prefs::kExtensionInstallDenyList);
1371 CheckAdminBlacklist();
1372 break;
1373 }
1374
[email protected]4814b512009-11-07 00:12:291375 default:
1376 NOTREACHED() << "Unexpected notification type.";
1377 }
1378}
1379
[email protected]377011d2010-07-20 04:18:501380bool ExtensionsService::HasApps() {
1381 if (!extensions_enabled_)
1382 return false;
1383
1384 for (ExtensionList::const_iterator it = extensions_.begin();
1385 it != extensions_.end(); ++it) {
1386 if ((*it)->is_app())
1387 return true;
1388 }
1389
1390 return false;
1391}
[email protected]4814b512009-11-07 00:12:291392
[email protected]6014d672008-12-05 00:38:251393// ExtensionsServicesBackend
1394
[email protected]894bb502009-05-21 22:39:571395ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:061396 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:081397 : frontend_(NULL),
1398 install_directory_(install_directory),
[email protected]8ef78fd2010-08-19 17:14:321399 alert_on_error_(false),
1400 external_extension_added_(false) {
[email protected]7577a5c52009-07-30 06:21:581401 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
1402 // pref data in the ctor and that is called on the UI thread. Would be better
1403 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:341404 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:011405 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:151406 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341407#if defined(OS_WIN)
1408 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:011409 linked_ptr<ExternalExtensionProvider>(
1410 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341411#endif
1412}
1413
1414ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:571415}
1416
[email protected]b0beaa662009-02-26 00:04:151417void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:571418 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151419 frontend_ = frontend;
1420
1421 // Explicit UI loads are always noisy.
1422 alert_on_error_ = true;
1423
[email protected]cc5da332009-03-04 08:02:511424 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:501425 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:451426
1427 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:161428 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:451429
[email protected]ab6f2b22009-07-28 23:28:371430 std::string error;
1431 Extension* extension = extension_file_util::LoadExtension(
1432 extension_path,
1433 false, // Don't require id
1434 &error);
1435
1436 if (!extension) {
1437 ReportExtensionLoadError(extension_path, error);
1438 return;
[email protected]0877fd92009-02-03 16:34:061439 }
[email protected]ab6f2b22009-07-28 23:28:371440
1441 extension->set_location(Extension::LOAD);
[email protected]e8c729a2010-03-09 19:55:191442
1443 // Report this as an installed extension so that it gets remembered in the
1444 // prefs.
1445 ChromeThread::PostTask(
1446 ChromeThread::UI, FROM_HERE,
1447 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
1448 extension, true));
[email protected]0877fd92009-02-03 16:34:061449}
1450
[email protected]6014d672008-12-05 00:38:251451void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:511452 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:061453 ChromeThread::PostTask(
1454 ChromeThread::UI, FROM_HERE,
1455 NewRunnableMethod(
1456 frontend_,
[email protected]d11c8e92009-10-20 23:26:401457 &ExtensionsService::ReportExtensionLoadError, extension_path,
1458 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:251459}
1460
[email protected]a1257b12009-06-12 02:51:341461bool ExtensionsServiceBackend::LookupExternalExtension(
1462 const std::string& id, Version** version, Extension::Location* location) {
1463 scoped_ptr<Version> extension_version;
1464 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1465 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011466 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341467 extension_version.reset(provider->RegisteredVersion(id, location));
1468 if (extension_version.get()) {
1469 if (version)
1470 *version = extension_version.release();
1471 return true;
1472 }
1473 }
1474 return false;
1475}
1476
[email protected]b0beaa662009-02-26 00:04:151477// Some extensions will autoupdate themselves externally from Chrome. These
1478// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191479// these, the extension will register its location in the the preferences file
1480// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151481// check that location for a .crx file, which it will then install locally if
1482// a new version is available.
1483void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571484 std::set<std::string> ids_to_ignore,
1485 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151486 // Note that this installation is intentionally silent (since it didn't
1487 // go through the front-end). Extensions that are registered in this
1488 // way are effectively considered 'pre-bundled', and so implicitly
1489 // trusted. In general, if something has HKLM or filesystem access,
1490 // they could install an extension manually themselves anyway.
1491 alert_on_error_ = false;
1492 frontend_ = frontend;
[email protected]8ef78fd2010-08-19 17:14:321493 external_extension_added_ = false;
[email protected]b0beaa662009-02-26 00:04:151494
[email protected]a1257b12009-06-12 02:51:341495 // Ask each external extension provider to give us a call back for each
[email protected]8ef78fd2010-08-19 17:14:321496 // extension they know about. See OnExternalExtension(File|UpdateUrl)Found.
1497
[email protected]a1257b12009-06-12 02:51:341498 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1499 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011500 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341501 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191502 }
[email protected]8ef78fd2010-08-19 17:14:321503
1504 if (external_extension_added_ && frontend->updater()) {
1505 ChromeThread::PostTask(
1506 ChromeThread::UI, FROM_HERE,
1507 NewRunnableMethod(
1508 frontend->updater(), &ExtensionUpdater::CheckNow));
1509 }
[email protected]b0beaa662009-02-26 00:04:151510}
1511
[email protected]ae09ca62009-08-21 19:46:461512void ExtensionsServiceBackend::CheckExternalUninstall(
1513 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1514 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341515 // Check if the providers know about this extension.
1516 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461517 if (i == external_extension_providers_.end()) {
1518 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1519 << location;
1520 return;
[email protected]b0beaa662009-02-26 00:04:151521 }
[email protected]25b34332009-06-05 21:53:191522
[email protected]ae09ca62009-08-21 19:46:461523 scoped_ptr<Version> version;
1524 version.reset(i->second->RegisteredVersion(id, NULL));
1525 if (version.get())
1526 return; // Yup, known extension, don't uninstall.
1527
1528 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061529 ChromeThread::PostTask(
1530 ChromeThread::UI, FROM_HERE,
1531 NewRunnableMethod(
1532 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151533}
1534
[email protected]a1257b12009-06-12 02:51:341535void ExtensionsServiceBackend::ClearProvidersForTesting() {
1536 external_extension_providers_.clear();
1537}
1538
1539void ExtensionsServiceBackend::SetProviderForTesting(
1540 Extension::Location location,
1541 ExternalExtensionProvider* test_provider) {
1542 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011543 external_extension_providers_[location] =
1544 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341545}
1546
[email protected]8ef78fd2010-08-19 17:14:321547void ExtensionsServiceBackend::OnExternalExtensionFileFound(
[email protected]7577a5c52009-07-30 06:21:581548 const std::string& id, const Version* version, const FilePath& path,
1549 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061550 ChromeThread::PostTask(
1551 ChromeThread::UI, FROM_HERE,
1552 NewRunnableMethod(
[email protected]8ef78fd2010-08-19 17:14:321553 frontend_, &ExtensionsService::OnExternalExtensionFileFound, id,
[email protected]95d29192009-10-30 01:49:061554 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191555}
[email protected]c6d474f82009-12-16 21:11:061556
[email protected]8ef78fd2010-08-19 17:14:321557void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound(
1558 const std::string& id, const GURL& update_url) {
1559 if (frontend_->GetExtensionById(id, true)) {
1560 // Already installed. Do not change the update URL that the extension set.
1561 return;
1562 }
1563
1564 frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url);
1565 external_extension_added_ |= true;
1566}
1567
[email protected]2111b1a2010-03-12 18:12:441568void ExtensionsServiceBackend::ReloadExtensionManifests(
[email protected]c6d474f82009-12-16 21:11:061569 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1570 base::TimeTicks start_time,
1571 scoped_refptr<ExtensionsService> frontend) {
1572 frontend_ = frontend;
1573
1574 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1575 ExtensionInfo* info = extensions_to_reload->at(i).get();
[email protected]2111b1a2010-03-12 18:12:441576 if (!ShouldReloadExtensionManifest(*info))
[email protected]c6d474f82009-12-16 21:11:061577 continue;
1578
1579 // We need to reload original manifest in order to localize properly.
1580 std::string error;
1581 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1582 info->extension_path, false, &error));
1583
1584 if (extension.get())
1585 extensions_to_reload->at(i)->extension_manifest.reset(
1586 static_cast<DictionaryValue*>(
1587 extension->manifest_value()->DeepCopy()));
1588 }
1589
1590 // Finish installing on UI thread.
1591 ChromeThread::PostTask(
1592 ChromeThread::UI, FROM_HERE,
1593 NewRunnableMethod(
1594 frontend_,
1595 &ExtensionsService::ContinueLoadAllExtensions,
1596 extensions_to_reload,
1597 start_time,
1598 true));
1599}