blob: 1dff71315805bb7381a2f41e9d08072c9954d9c0 [file] [log] [blame]
[email protected]1e8c93f2010-02-08 22:58:311// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]6014d672008-12-05 00:38:252// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extensions_service.h"
6
[email protected]654512b2010-09-01 02:09:427#include <algorithm>
8
[email protected]24b538a2010-02-27 01:22:449#include "base/basictypes.h"
[email protected]e2eb43112009-05-29 21:19:5410#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:2511#include "base/file_util.h"
[email protected]cc2c3432009-11-06 17:24:3612#include "base/histogram.h"
[email protected]cd500f72010-06-25 23:44:3213#include "base/stl_util-inl.h"
[email protected]24b538a2010-02-27 01:22:4414#include "base/string16.h"
[email protected]e83326f2010-07-31 17:29:2515#include "base/string_number_conversions.h"
[email protected]6014d672008-12-05 00:38:2516#include "base/string_util.h"
[email protected]cc2c3432009-11-06 17:24:3617#include "base/time.h"
[email protected]ce7f62e32010-08-10 23:43:5918#include "base/utf_string_conversions.h"
[email protected]cc655912009-01-29 23:19:1919#include "base/values.h"
[email protected]aa142702010-03-26 01:26:3320#include "base/version.h"
[email protected]15730c42009-09-03 00:03:2021#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2122#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2923#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5824#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5625#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3826#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2827#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]2c5e1e12010-06-10 13:14:4428#include "chrome/browser/extensions/extension_cookies_api.h"
[email protected]c10da4b02010-03-25 14:38:3229#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0530#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]14a000d2010-04-29 21:44:2431#include "chrome/browser/extensions/extension_error_reporter.h"
[email protected]de768a832009-10-30 05:25:0132#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5633#include "chrome/browser/extensions/extension_host.h"
[email protected]7596ce72010-08-30 05:10:4634#include "chrome/browser/extensions/extension_management_api.h"
[email protected]4814b512009-11-07 00:12:2935#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1736#include "chrome/browser/extensions/extension_updater.h"
[email protected]784688a62010-09-13 07:06:5237#include "chrome/browser/extensions/extension_webnavigation_api.h"
[email protected]a1257b12009-06-12 02:51:3438#include "chrome/browser/extensions/external_extension_provider.h"
39#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]56ad3792010-05-28 17:45:3340#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]37858e52010-08-26 00:22:0241#include "chrome/browser/prefs/pref_service.h"
[email protected]81e63782009-02-27 19:35:0942#include "chrome/browser/profile.h"
[email protected]56ad3792010-05-28 17:45:3343#include "chrome/browser/search_engines/template_url_model.h"
[email protected]2a3e22b12010-08-13 04:55:1744#include "chrome/browser/sync/glue/extension_sync_traits.h"
[email protected]11edd1e2010-07-21 00:14:5045#include "chrome/browser/sync/glue/extension_util.h"
[email protected]aab98a52009-12-02 03:22:3546#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5447#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5848#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4049#include "chrome/common/extensions/extension_constants.h"
[email protected]05c82182010-06-24 17:49:0850#include "chrome/common/extensions/extension_error_utils.h"
[email protected]7c927b62010-02-24 09:54:1351#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0652#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2653#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2954#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3455#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1956#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1457#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3258#include "googleurl/src/gurl.h"
[email protected]24b538a2010-02-27 01:22:4459#include "webkit/database/database_tracker.h"
60#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3161
[email protected]79db6232009-02-13 20:51:2062#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3463#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2064#endif
[email protected]6014d672008-12-05 00:38:2565
[email protected]5ef47ec2010-01-28 05:58:0566using base::Time;
67
[email protected]c6d474f82009-12-16 21:11:0668namespace errors = extension_manifest_errors;
69
[email protected]b6ab96d2009-08-20 18:58:1970namespace {
71
[email protected]29d0d4ac2010-09-08 21:10:3172#if defined(OS_LINUX)
73static const int kOmniboxIconPaddingLeft = 2;
74static const int kOmniboxIconPaddingRight = 2;
75#elif defined(OS_MACOSX)
76static const int kOmniboxIconPaddingLeft = 0;
77static const int kOmniboxIconPaddingRight = 2;
78#else
79static const int kOmniboxIconPaddingLeft = 0;
80static const int kOmniboxIconPaddingRight = 0;
81#endif
82
[email protected]c2c263c2010-08-13 21:59:4883bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
[email protected]2111b1a2010-03-12 18:12:4484 // Always reload LOAD extension manifests, because they can change on disk
85 // independent of the manifest in our prefs.
86 if (info.extension_location == Extension::LOAD)
87 return true;
88
89 // Otherwise, reload the manifest it needs to be relocalized.
90 return extension_l10n_util::ShouldRelocalizeManifest(info);
91}
92
[email protected]c2c263c2010-08-13 21:59:4893void GetExplicitOriginsInExtent(Extension* extension,
94 std::vector<GURL>* origins) {
95 typedef std::vector<URLPattern> PatternList;
96 std::set<GURL> set;
97 const PatternList& patterns = extension->web_extent().patterns();
98 for (PatternList::const_iterator pattern = patterns.begin();
99 pattern != patterns.end(); ++pattern) {
100 if (pattern->match_subdomains() || pattern->match_all_urls())
101 continue;
[email protected]654512b2010-09-01 02:09:42102 // Wildcard URL schemes won't parse into a valid GURL, so explicit schemes
103 // must be used.
104 PatternList explicit_patterns = pattern->ConvertToExplicitSchemes();
105 for (PatternList::const_iterator explicit_p = explicit_patterns.begin();
106 explicit_p != explicit_patterns.end(); ++explicit_p) {
107 GURL origin = GURL(explicit_p->GetAsString()).GetOrigin();
108 if (origin.is_valid()) {
109 set.insert(origin);
110 } else {
111 NOTREACHED();
112 }
113 }
[email protected]c2c263c2010-08-13 21:59:48114 }
115
116 for (std::set<GURL>::const_iterator unique = set.begin();
117 unique != set.end(); ++unique) {
118 origins->push_back(*unique);
119 }
120}
121
[email protected]c6d474f82009-12-16 21:11:06122} // namespace
[email protected]b6ab96d2009-08-20 18:58:19123
[email protected]8ef78fd2010-08-19 17:14:32124PendingExtensionInfo::PendingExtensionInfo(
125 const GURL& update_url,
126 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
127 bool is_from_sync,
128 bool install_silently,
129 bool enable_on_install,
130 bool enable_incognito_on_install)
[email protected]aa142702010-03-26 01:26:33131 : update_url(update_url),
[email protected]8ef78fd2010-08-19 17:14:32132 expected_crx_type(expected_crx_type),
133 is_from_sync(is_from_sync),
[email protected]4416c5a2010-06-26 01:28:57134 install_silently(install_silently),
135 enable_on_install(enable_on_install),
136 enable_incognito_on_install(enable_incognito_on_install) {}
[email protected]aa142702010-03-26 01:26:33137
138PendingExtensionInfo::PendingExtensionInfo()
139 : update_url(),
[email protected]8ef78fd2010-08-19 17:14:32140 expected_crx_type(PendingExtensionInfo::UNKNOWN),
141 is_from_sync(true),
[email protected]4416c5a2010-06-26 01:28:57142 install_silently(false),
143 enable_on_install(false),
144 enable_incognito_on_install(false) {}
[email protected]aa142702010-03-26 01:26:33145
[email protected]25b34332009-06-05 21:53:19146// ExtensionsService.
[email protected]6014d672008-12-05 00:38:25147
[email protected]cc655912009-01-29 23:19:19148const char* ExtensionsService::kInstallDirectoryName = "Extensions";
149const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42150
[email protected]06f37542010-08-14 18:26:28151namespace {
152
153bool IsGalleryDownloadURL(const GURL& download_url) {
[email protected]b7c2f252009-12-08 00:47:23154 if (StartsWithASCII(download_url.spec(),
[email protected]334e04a2010-06-24 23:34:44155 extension_urls::kMiniGalleryDownloadPrefix, false))
156 return true;
[email protected]b7c2f252009-12-08 00:47:23157
[email protected]06f37542010-08-14 18:26:28158 if (StartsWithASCII(download_url.spec(),
159 extension_urls::kGalleryDownloadPrefix, false))
160 return true;
[email protected]b7c2f252009-12-08 00:47:23161
[email protected]473ff6e2010-05-12 15:31:55162 // Allow command line gallery url to be referrer for the gallery downloads.
163 std::string command_line_gallery_url =
164 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
[email protected]2bce5e12010-05-13 01:18:07165 switches::kAppsGalleryURL);
[email protected]473ff6e2010-05-12 15:31:55166 if (!command_line_gallery_url.empty() &&
167 StartsWithASCII(download_url.spec(),
[email protected]dd9d6272010-09-09 17:33:18168 command_line_gallery_url, false))
[email protected]473ff6e2010-05-12 15:31:55169 return true;
[email protected]334e04a2010-06-24 23:34:44170
171 return false;
172}
173
[email protected]06f37542010-08-14 18:26:28174} // namespace
175
[email protected]334e04a2010-06-24 23:34:44176// static
177bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
178 const GURL& referrer_url) {
179 if (!IsGalleryDownloadURL(download_url))
180 return false;
181
182 if (StartsWithASCII(referrer_url.spec(),
183 extension_urls::kMiniGalleryBrowsePrefix, false))
184 return true;
185
186 if (StartsWithASCII(referrer_url.spec(),
187 Extension::ChromeStoreURL(), false))
188 return true;
189
190 // Allow command line gallery url to be referrer for the gallery downloads.
191 std::string command_line_gallery_url =
192 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
193 switches::kAppsGalleryURL);
194 if (!command_line_gallery_url.empty() &&
195 StartsWithASCII(referrer_url.spec(),
196 command_line_gallery_url, false))
197 return true;
[email protected]473ff6e2010-05-12 15:31:55198
[email protected]b7c2f252009-12-08 00:47:23199 return false;
200}
201
[email protected]ac025282009-12-16 19:16:38202bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
203 return StartsWithASCII(download_url.spec(),
204 extension_urls::kMiniGalleryDownloadPrefix,
205 false); // case_sensitive
206}
207
[email protected]81e63782009-02-27 19:35:09208ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08209 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23210 PrefService* prefs,
211 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17212 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12213 : profile_(profile),
214 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23215 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23216 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13217 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31218 ready_(false),
219 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08220 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23221 if (command_line->HasSwitch(switches::kDisableExtensions)) {
222 extensions_enabled_ = false;
223 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
224 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18225 }
[email protected]36a784c2009-06-23 06:21:08226
[email protected]4814b512009-11-07 00:12:29227 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
228 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16229 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]bc535ee52010-08-31 18:40:32230 NotificationService::AllSources());
[email protected]aa96d3a2010-08-21 08:45:25231 prefs->AddPrefObserver(prefs::kExtensionInstallAllowList, this);
232 prefs->AddPrefObserver(prefs::kExtensionInstallDenyList, this);
[email protected]4814b512009-11-07 00:12:29233
[email protected]93fd78f42009-07-10 16:43:17234 // Set up the ExtensionUpdater
235 if (autoupdate_enabled) {
236 int update_frequency = kDefaultUpdateFrequencySeconds;
237 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]e83326f2010-07-31 17:29:25238 base::StringToInt(command_line->GetSwitchValueASCII(
239 switches::kExtensionsUpdateFrequency),
240 &update_frequency);
[email protected]93fd78f42009-07-10 16:43:17241 }
[email protected]95d29192009-10-30 01:49:06242 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17243 }
244
[email protected]95d29192009-10-30 01:49:06245 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]b671760b2010-07-15 21:13:47246
[email protected]aa96d3a2010-08-21 08:45:25247 // Use monochrome icons for Omnibox icons.
[email protected]29d0d4ac2010-09-08 21:10:31248 omnibox_popup_icon_manager_.set_monochrome(true);
[email protected]b671760b2010-07-15 21:13:47249 omnibox_icon_manager_.set_monochrome(true);
[email protected]29d0d4ac2010-09-08 21:10:31250 omnibox_icon_manager_.set_padding(gfx::Insets(0, kOmniboxIconPaddingLeft,
251 0, kOmniboxIconPaddingRight));
[email protected]6014d672008-12-05 00:38:25252}
253
254ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32255 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17256 if (updater_.get()) {
257 updater_->Stop();
258 }
[email protected]6014d672008-12-05 00:38:25259}
260
[email protected]c5ae74ab2010-04-15 18:14:37261void ExtensionsService::InitEventRouters() {
262 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
263 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]56ee0152010-06-16 01:54:42264 ExtensionBrowserEventRouter::GetInstance()->Init(profile_);
[email protected]c5ae74ab2010-04-15 18:14:37265 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
266 profile_->GetBookmarkModel());
[email protected]2c5e1e12010-06-10 13:14:44267 ExtensionCookiesEventRouter::GetInstance()->Init();
[email protected]7596ce72010-08-30 05:10:46268 ExtensionManagementEventRouter::GetInstance()->Init();
[email protected]784688a62010-09-13 07:06:52269 ExtensionWebNavigationEventRouter::GetInstance()->Init();
[email protected]c5ae74ab2010-04-15 18:14:37270}
271
[email protected]9f1087e2009-06-15 17:29:32272void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29273 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17274 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32275
[email protected]95dd38f2009-10-20 20:09:15276 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
277 // the first extension, because its members listen for loaded notifications.
278 g_browser_process->resource_dispatcher_host();
279
[email protected]9f1087e2009-06-15 17:29:32280 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57281
[email protected]9f1087e2009-06-15 17:29:32282 // TODO(erikkay) this should probably be deferred to a future point
283 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17284 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57285
[email protected]9f1087e2009-06-15 17:29:32286 // TODO(erikkay) this should probably be deferred as well.
287 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25288}
289
[email protected]3cf4f0992009-02-03 23:00:30290void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16291 scoped_refptr<CrxInstaller> installer(
292 new CrxInstaller(install_directory_,
293 this, // frontend
294 NULL)); // no client (silent install)
295 installer->set_allow_privilege_increase(true);
296 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30297}
298
[email protected]aa142702010-03-26 01:26:33299namespace {
300 // TODO(akalin): Put this somewhere where both crx_installer.cc and
301 // this file can use it.
302 void DeleteFileHelper(const FilePath& path, bool recursive) {
303 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
304 file_util::Delete(path, recursive);
305 }
306} // namespace
307
[email protected]e957fe52009-06-23 16:51:05308void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34309 const FilePath& extension_path,
310 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33311 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
[email protected]8ef78fd2010-08-19 17:14:32312 bool is_pending_extension = (it != pending_extensions_.end());
313
314 if (!is_pending_extension &&
[email protected]aa142702010-03-26 01:26:33315 !GetExtensionByIdInternal(id, true, true)) {
316 LOG(WARNING) << "Will not update extension " << id
317 << " because it is not installed or pending";
318 // Delete extension_path since we're not creating a CrxInstaller
319 // that would do it for us.
320 ChromeThread::PostTask(
321 ChromeThread::FILE, FROM_HERE,
322 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49323 return;
[email protected]e957fe52009-06-23 16:51:05324 }
325
[email protected]aa142702010-03-26 01:26:33326 // We want a silent install only for non-pending extensions and
327 // pending extensions that have install_silently set.
328 ExtensionInstallUI* client =
[email protected]8ef78fd2010-08-19 17:14:32329 (!is_pending_extension || it->second.install_silently) ?
[email protected]aa142702010-03-26 01:26:33330 NULL : new ExtensionInstallUI(profile_);
331
[email protected]6dfbbf82010-03-12 23:09:16332 scoped_refptr<CrxInstaller> installer(
333 new CrxInstaller(install_directory_,
334 this, // frontend
[email protected]aa142702010-03-26 01:26:33335 client));
[email protected]6dfbbf82010-03-12 23:09:16336 installer->set_expected_id(id);
[email protected]8ef78fd2010-08-19 17:14:32337 if (is_pending_extension && !it->second.is_from_sync)
338 installer->set_install_source(Extension::EXTERNAL_PREF_DOWNLOAD);
[email protected]6dfbbf82010-03-12 23:09:16339 installer->set_delete_source(true);
[email protected]5c8516202010-03-18 21:43:34340 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16341 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05342}
343
[email protected]8ef78fd2010-08-19 17:14:32344void ExtensionsService::AddPendingExtensionFromSync(
[email protected]aa142702010-03-26 01:26:33345 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32346 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
347 bool install_silently, bool enable_on_install,
348 bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33349 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25350 LOG(DFATAL) << "Trying to add pending extension " << id
351 << " which already exists";
[email protected]aa142702010-03-26 01:26:33352 return;
353 }
[email protected]4416c5a2010-06-26 01:28:57354 AddPendingExtensionInternal(
[email protected]8ef78fd2010-08-19 17:14:32355 id, update_url, expected_crx_type, true, install_silently,
[email protected]4416c5a2010-06-26 01:28:57356 enable_on_install, enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33357}
358
[email protected]8ef78fd2010-08-19 17:14:32359void ExtensionsService::AddPendingExtensionFromExternalUpdateUrl(
360 const std::string& id, const GURL& update_url) {
361 // Add the extension to this list of extensions to update.
362 // We do not know if the id refers to a theme, so make is_theme unknown.
363 const PendingExtensionInfo::ExpectedCrxType kExpectedCrxType =
364 PendingExtensionInfo::UNKNOWN;
365 const bool kIsFromSync = false;
366 const bool kInstallSilently = true;
367 const bool kEnableOnInstall = true;
368 const bool kEnableIncognitoOnInstall = false;
369
370 if (GetExtensionByIdInternal(id, true, true)) {
371 LOG(DFATAL) << "Trying to add extension " << id
372 << " by external update, but it is already installed.";
373 return;
374 }
375
376 AddPendingExtensionInternal(id, update_url, kExpectedCrxType, kIsFromSync,
377 kInstallSilently, kEnableOnInstall,
378 kEnableIncognitoOnInstall);
379}
380
[email protected]aa142702010-03-26 01:26:33381void ExtensionsService::AddPendingExtensionInternal(
382 const std::string& id, const GURL& update_url,
[email protected]8ef78fd2010-08-19 17:14:32383 PendingExtensionInfo::ExpectedCrxType expected_crx_type,
384 bool is_from_sync, bool install_silently,
[email protected]4416c5a2010-06-26 01:28:57385 bool enable_on_install, bool enable_incognito_on_install) {
[email protected]aa142702010-03-26 01:26:33386 pending_extensions_[id] =
[email protected]8ef78fd2010-08-19 17:14:32387 PendingExtensionInfo(update_url, expected_crx_type, is_from_sync,
388 install_silently, enable_on_install,
389 enable_incognito_on_install);
[email protected]aa142702010-03-26 01:26:33390}
391
[email protected]9cddd4702009-07-27 22:09:40392void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06393 FilePath path;
[email protected]61b411612009-11-10 23:17:41394 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40395
[email protected]f17dbd42010-08-16 23:21:10396 // Disable the extension if it's loaded. It might not be loaded if it crashed.
[email protected]b65272f2009-08-31 15:47:06397 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29398 // If the extension has an inspector open for its background page, detach
399 // the inspector and hang onto a cookie for it, so that we can reattach
400 // later.
401 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
402 ExtensionHost* host = manager->GetBackgroundHostForExtension(
403 current_extension);
404 if (host) {
405 // Look for an open inspector for the background page.
406 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
407 host->render_view_host());
408 if (devtools_cookie >= 0)
409 orphaned_dev_tools_[extension_id] = devtools_cookie;
410 }
411
[email protected]b65272f2009-08-31 15:47:06412 path = current_extension->path();
[email protected]f17dbd42010-08-16 23:21:10413 DisableExtension(extension_id);
414 disabled_extension_paths_[extension_id] = path;
[email protected]1eb175082010-02-10 09:26:16415 } else {
416 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06417 }
418
[email protected]e6090e42010-03-23 22:44:08419 // Check the installed extensions to see if what we're reloading was already
420 // installed.
421 scoped_ptr<ExtensionInfo> installed_extension(
422 extension_prefs_->GetInstalledExtensionInfo(extension_id));
423 if (installed_extension.get() &&
424 installed_extension->extension_manifest.get()) {
425 LoadInstalledExtension(*installed_extension, false);
426 } else {
427 // We should always be able to remember the extension's path. If it's not in
428 // the map, someone failed to update |unloaded_extension_paths_|.
429 CHECK(!path.empty());
430 LoadExtension(path);
431 }
[email protected]9cddd4702009-07-27 22:09:40432}
433
[email protected]27b985d2009-06-25 17:53:15434void ExtensionsService::UninstallExtension(const std::string& extension_id,
435 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39436 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25437
[email protected]e7afe2452010-08-22 16:19:13438 // Callers should not send us nonexistent extensions.
[email protected]e72e8eb82009-06-18 17:21:51439 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32440
[email protected]4c793f02010-08-18 20:55:45441 // Notify interested parties that we're uninstalling this extension.
442 NotificationService::current()->Notify(
443 NotificationType::EXTENSION_UNINSTALLED,
444 Source<Profile>(profile_),
445 Details<Extension>(extension));
446
[email protected]831aa212010-03-26 13:55:19447 // Get hold of information we need after unloading, since the extension
448 // pointer will be invalid then.
449 GURL extension_url(extension->url());
450 Extension::Location location(extension->location());
451
452 // Also copy the extension identifier since the reference might have been
453 // obtained via Extension::id().
454 std::string extension_id_copy(extension_id);
455
[email protected]56ad3792010-05-28 17:45:33456 if (profile_->GetTemplateURLModel())
457 profile_->GetTemplateURLModel()->UnregisterExtensionKeyword(extension);
458
[email protected]831aa212010-03-26 13:55:19459 // Unload before doing more cleanup to ensure that nothing is hanging on to
460 // any of these resources.
461 UnloadExtension(extension_id);
462
463 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
464 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32465
466 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19467 if (Extension::LOAD != location) {
[email protected]95d29192009-10-30 01:49:06468 ChromeThread::PostTask(
469 ChromeThread::FILE, FROM_HERE,
470 NewRunnableFunction(
[email protected]ca3dbf52010-05-19 22:27:06471 &extension_file_util::UninstallExtension,
472 install_directory_,
473 extension_id_copy));
[email protected]9f1087e2009-06-15 17:29:32474 }
475
[email protected]c10da4b02010-03-25 14:38:32476 ClearExtensionData(extension_url);
477}
478
479void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
480 scoped_refptr<ExtensionDataDeleter> deleter(
481 new ExtensionDataDeleter(profile_, extension_url));
482 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32483}
484
[email protected]0c6da502009-08-14 22:32:39485void ExtensionsService::EnableExtension(const std::string& extension_id) {
486 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
487 if (!extension) {
488 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
489 return;
490 }
491
[email protected]e8c729a2010-03-09 19:55:19492 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52493
[email protected]0c6da502009-08-14 22:32:39494 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39495 extensions_.push_back(extension);
496 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
497 disabled_extensions_.end(),
498 extension);
499 disabled_extensions_.erase(iter);
500
[email protected]86c008e82009-08-28 20:26:05501 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
502 extension->GetChromeURLOverrides());
503
[email protected]62d30f42009-10-01 22:36:06504 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35505 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39506}
507
[email protected]1784e83a2009-09-08 21:01:52508void ExtensionsService::DisableExtension(const std::string& extension_id) {
509 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15510 // The extension may have been disabled already.
511 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52512 return;
[email protected]1784e83a2009-09-08 21:01:52513
[email protected]e8c729a2010-03-09 19:55:19514 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52515
516 // Move it over to the disabled list.
517 disabled_extensions_.push_back(extension);
518 ExtensionList::iterator iter = std::find(extensions_.begin(),
519 extensions_.end(),
520 extension);
521 extensions_.erase(iter);
522
523 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
524 extension->GetChromeURLOverrides());
525
[email protected]62d30f42009-10-01 22:36:06526 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35527 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52528}
529
[email protected]9f1087e2009-06-15 17:29:32530void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06531 ChromeThread::PostTask(
532 ChromeThread::FILE, FROM_HERE,
533 NewRunnableMethod(
534 backend_.get(),
535 &ExtensionsServiceBackend::LoadSingleExtension,
536 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32537}
538
[email protected]1952c7d2010-03-04 23:48:34539void ExtensionsService::LoadComponentExtensions() {
540 for (RegisteredComponentExtensions::iterator it =
541 component_extension_manifests_.begin();
542 it != component_extension_manifests_.end(); ++it) {
543 JSONStringValueSerializer serializer(it->manifest);
[email protected]ba399672010-04-06 15:42:39544 scoped_ptr<Value> manifest(serializer.Deserialize(NULL, NULL));
[email protected]999731f2010-03-22 19:13:53545 if (!manifest.get()) {
[email protected]960ef8ae2010-05-06 17:12:29546 DLOG(ERROR) << "Failed to retrieve manifest for extension";
[email protected]999731f2010-03-22 19:13:53547 continue;
548 }
[email protected]1952c7d2010-03-04 23:48:34549
550 scoped_ptr<Extension> extension(new Extension(it->root_directory));
551 extension->set_location(Extension::COMPONENT);
552
553 std::string error;
554 if (!extension->InitFromValue(
555 *static_cast<DictionaryValue*>(manifest.get()),
556 true, // require key
557 &error)) {
[email protected]4fdbc1492010-07-01 01:20:59558 NOTREACHED() << error;
[email protected]1952c7d2010-03-04 23:48:34559 return;
560 }
561
[email protected]fe13bf62010-08-26 14:33:19562 // In order for the --apps-gallery-url switch to work with the gallery
563 // process isolation, we must insert any provided value into the component
564 // app's launch url and web extent.
[email protected]7596ce72010-08-30 05:10:46565 if (extension->id() == extension_misc::kWebStoreAppId) {
[email protected]fe13bf62010-08-26 14:33:19566 GURL gallery_url(CommandLine::ForCurrentProcess()
567 ->GetSwitchValueASCII(switches::kAppsGalleryURL));
568 if (gallery_url.is_valid()) {
569 extension->set_launch_web_url(gallery_url.spec());
570 URLPattern pattern(URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS);
571 pattern.Parse(gallery_url.spec());
572 pattern.set_path(pattern.path() + '*');
573 extension->web_extent().AddPattern(pattern);
574 }
575 }
576
[email protected]1952c7d2010-03-04 23:48:34577 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
578 // increase.
579 }
580}
581
[email protected]9f1087e2009-06-15 17:29:32582void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36583 base::TimeTicks start_time = base::TimeTicks::Now();
584
[email protected]1952c7d2010-03-04 23:48:34585 // Load any component extensions.
586 LoadComponentExtensions();
587
[email protected]e72e8eb82009-06-18 17:21:51588 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06589 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08590 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06591
592 // If any extensions need localization, we bounce them all to the file thread
593 // for re-reading and localization.
594 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44595 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]c6d474f82009-12-16 21:11:06596 ChromeThread::PostTask(
597 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
598 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44599 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06600 info.release(), // Callee takes ownership of the memory.
601 start_time,
602 scoped_refptr<ExtensionsService>(this)));
603 return;
604 }
605 }
606
607 // Don't update prefs.
608 // Callee takes ownership of the memory.
609 ContinueLoadAllExtensions(info.release(), start_time, false);
610}
611
612void ExtensionsService::ContinueLoadAllExtensions(
613 ExtensionPrefs::ExtensionsInfo* extensions_info,
614 base::TimeTicks start_time,
615 bool write_to_prefs) {
616 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
617
618 for (size_t i = 0; i < info->size(); ++i) {
619 LoadInstalledExtension(*info->at(i), write_to_prefs);
620 }
621
[email protected]ae09ca62009-08-21 19:46:46622 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36623
624 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
625 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
626
[email protected]1952c7d2010-03-04 23:48:34627 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
628 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36629
[email protected]1952c7d2010-03-04 23:48:34630 int user_script_count = 0;
631 int extension_count = 0;
632 int theme_count = 0;
633 int external_count = 0;
634 int page_action_count = 0;
635 int browser_action_count = 0;
636 ExtensionList::iterator ex;
637 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
638 // Don't count component extensions, since they are only extensions as an
639 // implementation detail.
640 if ((*ex)->location() == Extension::COMPONENT)
641 continue;
642
[email protected]e8c729a2010-03-09 19:55:19643 // Don't count unpacked extensions, since they're a developer-specific
644 // feature.
645 if ((*ex)->location() == Extension::LOAD)
646 continue;
647
[email protected]3ba0fd32010-06-19 05:39:10648 if ((*ex)->is_theme()) {
[email protected]1952c7d2010-03-04 23:48:34649 theme_count++;
650 } else if ((*ex)->converted_from_user_script()) {
651 user_script_count++;
652 } else {
653 extension_count++;
[email protected]cc2c3432009-11-06 17:24:36654 }
[email protected]1952c7d2010-03-04 23:48:34655 if (Extension::IsExternalLocation((*ex)->location())) {
656 external_count++;
657 }
658 if ((*ex)->page_action() != NULL) {
659 page_action_count++;
660 }
661 if ((*ex)->browser_action() != NULL) {
662 browser_action_count++;
663 }
[email protected]cc2c3432009-11-06 17:24:36664 }
[email protected]1952c7d2010-03-04 23:48:34665 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
666 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
667 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
668 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
669 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
670 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
671 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:46672}
673
[email protected]c6d474f82009-12-16 21:11:06674void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
675 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46676 std::string error;
677 Extension* extension = NULL;
[email protected]306a2bd2010-08-11 14:56:36678 if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) {
679 error = errors::kDisabledByPolicy;
680 } else if (info.extension_manifest.get()) {
[email protected]c6d474f82009-12-16 21:11:06681 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:19682 bool require_key = info.extension_location != Extension::LOAD;
683 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:46684 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46685 } else {
[email protected]c6d474f82009-12-16 21:11:06686 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46687 }
688
689 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06690 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40691 error,
692 NotificationType::EXTENSION_INSTALL_ERROR,
693 false);
[email protected]ae09ca62009-08-21 19:46:46694 return;
695 }
696
[email protected]c6d474f82009-12-16 21:11:06697 extension->set_location(info.extension_location);
698
699 if (write_to_prefs)
700 extension_prefs_->UpdateManifest(extension);
701
[email protected]2a409532009-08-28 19:39:44702 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46703
[email protected]c6d474f82009-12-16 21:11:06704 if (info.extension_location == Extension::EXTERNAL_PREF ||
705 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06706 ChromeThread::PostTask(
707 ChromeThread::FILE, FROM_HERE,
708 NewRunnableMethod(
[email protected]f17dbd42010-08-16 23:21:10709 backend_.get(),
710 &ExtensionsServiceBackend::CheckExternalUninstall,
711 scoped_refptr<ExtensionsService>(this),
712 info.extension_id,
713 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46714 }
[email protected]9f1087e2009-06-15 17:29:32715}
716
[email protected]62d30f42009-10-01 22:36:06717void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
[email protected]57a777f72010-03-31 01:09:42718 // The ChromeURLRequestContexts need to be first to know that the extension
[email protected]62d30f42009-10-01 22:36:06719 // was loaded, otherwise a race can arise where a renderer that is created
720 // for the extension may try to load an extension URL with an extension id
[email protected]57a777f72010-03-31 01:09:42721 // that the request context doesn't yet know about. The profile is responsible
722 // for ensuring its URLRequestContexts appropriately discover the loaded
723 // extension.
724 if (profile_) {
725 profile_->RegisterExtensionWithRequestContexts(extension);
[email protected]24b538a2010-02-27 01:22:44726
727 // Check if this permission requires unlimited storage quota
[email protected]c2c263c2010-08-13 21:59:48728 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
729 GrantUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:42730
731 // If the extension is an app, protect its local storage from
732 // "Clear browsing data."
733 if (extension->is_app())
734 GrantProtectedStorage(extension);
[email protected]62d30f42009-10-01 22:36:06735 }
736
[email protected]57a777f72010-03-31 01:09:42737 LOG(INFO) << "Sending EXTENSION_LOADED";
738
[email protected]62d30f42009-10-01 22:36:06739 NotificationService::current()->Notify(
740 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34741 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06742 Details<Extension>(extension));
743}
744
745void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
746 LOG(INFO) << "Sending EXTENSION_UNLOADED";
747
748 NotificationService::current()->Notify(
749 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34750 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06751 Details<Extension>(extension));
752
[email protected]57a777f72010-03-31 01:09:42753 if (profile_) {
754 profile_->UnregisterExtensionWithRequestContexts(extension);
755
756 // Check if this permission required unlimited storage quota, reset its
757 // in-memory quota.
[email protected]c2c263c2010-08-13 21:59:48758 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission))
759 RevokeUnlimitedStorage(extension);
[email protected]654512b2010-09-01 02:09:42760
761 // If this is an app, then stop protecting its storage so it can be deleted.
762 if (extension->is_app())
763 RevokeProtectedStorage(extension);
764 }
765}
766
767void ExtensionsService::GrantProtectedStorage(Extension* extension) {
768 DCHECK(extension->is_app()) << "Only Apps are allowed protected storage.";
769 std::vector<GURL> origins;
770 GetExplicitOriginsInExtent(extension, &origins);
771 for (size_t i = 0; i < origins.size(); ++i)
772 ++protected_storage_map_[origins[i]];
773}
774
775void ExtensionsService::RevokeProtectedStorage(Extension* extension) {
776 DCHECK(extension->is_app()) << "Attempting to revoke protected storage from "
777 << " a non-app extension.";
778 std::vector<GURL> origins;
779 GetExplicitOriginsInExtent(extension, &origins);
780 for (size_t i = 0; i < origins.size(); ++i) {
781 const GURL& origin = origins[i];
782 DCHECK(protected_storage_map_[origin] > 0);
783 if (--protected_storage_map_[origin] <= 0)
784 protected_storage_map_.erase(origin);
[email protected]c2c263c2010-08-13 21:59:48785 }
786}
787
788void ExtensionsService::GrantUnlimitedStorage(Extension* extension) {
789 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
790 std::vector<GURL> origins;
791 GetExplicitOriginsInExtent(extension, &origins);
792 origins.push_back(extension->url());
793
794 for (size_t i = 0; i < origins.size(); ++i) {
795 const GURL& origin = origins[i];
796 if (++unlimited_storage_map_[origin] == 1) {
[email protected]57a777f72010-03-31 01:09:42797 string16 origin_identifier =
[email protected]c2c263c2010-08-13 21:59:48798 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
799 ChromeThread::PostTask(
800 ChromeThread::FILE, FROM_HERE,
801 NewRunnableMethod(
802 profile_->GetDatabaseTracker(),
803 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
804 origin_identifier,
805 kint64max));
806 ChromeThread::PostTask(
807 ChromeThread::IO, FROM_HERE,
808 NewRunnableMethod(
809 profile_->GetAppCacheService(),
810 &ChromeAppCacheService::SetOriginQuotaInMemory,
811 origin,
812 kint64max));
813 }
814 }
815}
816
817void ExtensionsService::RevokeUnlimitedStorage(Extension* extension) {
818 DCHECK(extension->HasApiPermission(Extension::kUnlimitedStoragePermission));
819 std::vector<GURL> origins;
820 GetExplicitOriginsInExtent(extension, &origins);
821 origins.push_back(extension->url());
822
823 for (size_t i = 0; i < origins.size(); ++i) {
824 const GURL& origin = origins[i];
825 DCHECK(unlimited_storage_map_[origin] > 0);
826 if (--unlimited_storage_map_[origin] == 0) {
827 unlimited_storage_map_.erase(origin);
828 string16 origin_identifier =
829 webkit_database::DatabaseUtil::GetOriginIdentifier(origin);
[email protected]95d29192009-10-30 01:49:06830 ChromeThread::PostTask(
[email protected]57a777f72010-03-31 01:09:42831 ChromeThread::FILE, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31832 NewRunnableMethod(
[email protected]57a777f72010-03-31 01:09:42833 profile_->GetDatabaseTracker(),
834 &webkit_database::DatabaseTracker::ResetOriginQuotaInMemory,
835 origin_identifier));
[email protected]c2c263c2010-08-13 21:59:48836 ChromeThread::PostTask(
837 ChromeThread::IO, FROM_HERE,
838 NewRunnableMethod(
839 profile_->GetAppCacheService(),
840 &ChromeAppCacheService::ResetOriginQuotaInMemory,
841 origin));
[email protected]62d30f42009-10-01 22:36:06842 }
843 }
844}
845
[email protected]6b75ec32009-08-14 06:37:18846void ExtensionsService::UpdateExtensionBlacklist(
847 const std::vector<std::string>& blacklist) {
848 // Use this set to indicate if an extension in the blacklist has been used.
849 std::set<std::string> blacklist_set;
850 for (unsigned int i = 0; i < blacklist.size(); ++i) {
851 if (Extension::IdIsValid(blacklist[i])) {
852 blacklist_set.insert(blacklist[i]);
853 }
854 }
855 extension_prefs_->UpdateBlacklist(blacklist_set);
856 std::vector<std::string> to_be_removed;
857 // Loop current extensions, unload installed extensions.
858 for (ExtensionList::const_iterator iter = extensions_.begin();
859 iter != extensions_.end(); ++iter) {
860 Extension* extension = (*iter);
861 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
862 to_be_removed.push_back(extension->id());
863 }
864 }
865
866 // UnloadExtension will change the extensions_ list. So, we should
867 // call it outside the iterator loop.
868 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
869 UnloadExtension(to_be_removed[i]);
870 }
871}
872
[email protected]aa96d3a2010-08-21 08:45:25873void ExtensionsService::DestroyingProfile() {
874 profile_->GetPrefs()->RemovePrefObserver(
875 prefs::kExtensionInstallAllowList, this);
876 profile_->GetPrefs()->RemovePrefObserver(
877 prefs::kExtensionInstallDenyList, this);
878
879 profile_ = NULL;
880}
881
882void ExtensionsService::CheckAdminBlacklist() {
883 std::vector<std::string> to_be_removed;
884 // Loop through extensions list, unload installed extensions.
885 for (ExtensionList::const_iterator iter = extensions_.begin();
886 iter != extensions_.end(); ++iter) {
887 Extension* extension = (*iter);
888 if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id()))
889 to_be_removed.push_back(extension->id());
890 }
891
892 // UnloadExtension will change the extensions_ list. So, we should
893 // call it outside the iterator loop.
894 for (unsigned int i = 0; i < to_be_removed.size(); ++i)
895 UnloadExtension(to_be_removed[i]);
896}
897
[email protected]cb0ce1e022010-03-10 19:54:41898bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
899 // If this is a component extension we always allow it to work in incognito
900 // mode.
901 if (extension->location() == Extension::COMPONENT)
902 return true;
903
904 // Check the prefs.
905 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:50906}
[email protected]55a35692010-02-11 23:25:21907
[email protected]cb0ce1e022010-03-10 19:54:41908void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:50909 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:41910 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:24911
[email protected]568f33d2010-08-04 17:06:41912 // Broadcast unloaded and loaded events to update browser state. Only bother
913 // if the extension is actually enabled, since there is no UI otherwise.
914 bool is_enabled = std::find(extensions_.begin(), extensions_.end(),
915 extension) != extensions_.end();
916 if (is_enabled) {
917 NotifyExtensionUnloaded(extension);
918 NotifyExtensionLoaded(extension);
919 }
[email protected]55a35692010-02-11 23:25:21920}
921
[email protected]05c82182010-06-24 17:49:08922bool ExtensionsService::AllowFileAccess(const Extension* extension) {
923 return (CommandLine::ForCurrentProcess()->HasSwitch(
[email protected]334e04a2010-06-24 23:34:44924 switches::kDisableExtensionsFileAccessCheck) ||
[email protected]05c82182010-06-24 17:49:08925 extension_prefs_->AllowFileAccess(extension->id()));
926}
927
928void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
929 extension_prefs_->SetAllowFileAccess(extension->id(), allow);
930 NotificationService::current()->Notify(
931 NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
932 Source<Profile>(profile_),
933 Details<Extension>(extension));
934}
935
936bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
937 const GURL& url,
938 std::string* error) const {
939 // No extensions are allowed to execute script on the gallery because that
940 // would allow extensions to manipulate their own install pages.
[email protected]638d45d2010-07-09 17:35:08941 if (url.host() == GURL(Extension::ChromeStoreURL()).host()
942 && !CommandLine::ForCurrentProcess()->HasSwitch(
943 switches::kAllowScriptingGallery)) {
[email protected]05c82182010-06-24 17:49:08944 if (error)
945 *error = errors::kCannotScriptGallery;
946 return false;
947 }
948
949 if (extension->HasHostPermission(url))
950 return true;
951
952 if (error) {
953 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
954 url.spec());
955 }
956
957 return false;
958}
959
[email protected]93fd78f42009-07-10 16:43:17960void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32961 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58962 // TODO(aa): Why pass this list into the provider, why not just filter it
963 // later?
[email protected]9f1087e2009-06-15 17:29:32964 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51965 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06966 ChromeThread::PostTask(
967 ChromeThread::FILE, FROM_HERE,
968 NewRunnableMethod(
969 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
970 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32971}
972
973void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09974 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39975 scoped_ptr<Extension> extension(
976 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25977
[email protected]e7afe2452010-08-22 16:19:13978 // Callers should not send us nonexistent extensions.
[email protected]0c6da502009-08-14 22:32:39979 CHECK(extension.get());
980
[email protected]1eb175082010-02-10 09:26:16981 // Keep information about the extension so that we can reload it later
982 // even if it's not permanently installed.
983 unloaded_extension_paths_[extension->id()] = extension->path();
984
[email protected]f17dbd42010-08-16 23:21:10985 // Clean up if the extension is meant to be enabled after a reload.
986 disabled_extension_paths_.erase(extension->id());
987
[email protected]86c008e82009-08-28 20:26:05988 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
989 extension->GetChromeURLOverrides());
990
[email protected]0c6da502009-08-14 22:32:39991 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
992 disabled_extensions_.end(),
993 extension.get());
994 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39995 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47996 NotificationService::current()->Notify(
997 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34998 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47999 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:391000 return;
1001 }
1002
1003 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:571004
[email protected]631cf822009-05-15 07:01:251005 // Remove the extension from our list.
1006 extensions_.erase(iter);
1007
[email protected]62d30f42009-10-01 22:36:061008 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:351009 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:251010}
1011
[email protected]9f1087e2009-06-15 17:29:321012void ExtensionsService::UnloadAllExtensions() {
[email protected]cd500f72010-06-25 23:44:321013 STLDeleteContainerPointers(extensions_.begin(), extensions_.end());
[email protected]9f1087e2009-06-15 17:29:321014 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:291015
[email protected]cd500f72010-06-25 23:44:321016 STLDeleteContainerPointers(disabled_extensions_.begin(),
1017 disabled_extensions_.end());
1018 disabled_extensions_.clear();
1019
[email protected]c6e4a3412009-06-24 15:45:291020 // TODO(erikkay) should there be a notification for this? We can't use
1021 // EXTENSION_UNLOADED since that implies that the extension has been disabled
1022 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:321023}
1024
1025void ExtensionsService::ReloadExtensions() {
1026 UnloadAllExtensions();
1027 LoadAllExtensions();
1028}
1029
1030void ExtensionsService::GarbageCollectExtensions() {
[email protected]ba399672010-04-06 15:42:391031 if (extension_prefs_->pref_service()->read_only())
1032 return;
1033
[email protected]ca3dbf52010-05-19 22:27:061034 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
1035 extension_prefs_->GetInstalledExtensionsInfo());
1036
1037 std::map<std::string, FilePath> extension_paths;
1038 for (size_t i = 0; i < info->size(); ++i)
1039 extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
1040
[email protected]95d29192009-10-30 01:49:061041 ChromeThread::PostTask(
1042 ChromeThread::FILE, FROM_HERE,
1043 NewRunnableFunction(
1044 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]ca3dbf52010-05-19 22:27:061045 extension_paths));
[email protected]3cf4f0992009-02-03 23:00:301046}
1047
[email protected]e72e8eb82009-06-18 17:21:511048void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:131049 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:171050 if (updater_.get()) {
1051 updater_->Start();
1052 }
[email protected]e72e8eb82009-06-18 17:21:511053 NotificationService::current()->Notify(
1054 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:341055 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:511056 NotificationService::NoDetails());
1057}
1058
[email protected]6d2e60bd2010-06-03 22:37:391059void ExtensionsService::OnExtensionLoaded(Extension* extension,
[email protected]2a409532009-08-28 19:39:441060 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:461061 // Ensure extension is deleted unless we transfer ownership.
1062 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:321063
[email protected]1eb175082010-02-10 09:26:161064 // The extension is now loaded, remove its data from unloaded extension map.
1065 unloaded_extension_paths_.erase(extension->id());
1066
[email protected]f17dbd42010-08-16 23:21:101067 // If the extension was disabled for a reload, then enable it.
1068 if (disabled_extension_paths_.erase(extension->id()) > 0)
1069 EnableExtension(extension->id());
1070
[email protected]ceefd3d2010-03-12 09:10:291071 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
1072 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:461073 if (extensions_enabled() ||
[email protected]3ba0fd32010-06-19 05:39:101074 extension->is_theme() ||
[email protected]ae09ca62009-08-21 19:46:461075 extension->location() == Extension::LOAD ||
1076 Extension::IsExternalLocation(extension->location())) {
1077 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
1078 if (old) {
[email protected]ca3dbf52010-05-19 22:27:061079 // CrxInstaller should have guaranteed that we aren't downgrading.
1080 CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
[email protected]0c6da502009-08-14 22:32:391081
[email protected]ca3dbf52010-05-19 22:27:061082 bool allow_silent_upgrade =
1083 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
1084 old, extension);
[email protected]1e8c93f2010-02-08 22:58:311085
[email protected]ca3dbf52010-05-19 22:27:061086 // Extensions get upgraded if silent upgrades are allowed, otherwise
1087 // they get disabled.
1088 if (allow_silent_upgrade) {
1089 old->set_being_upgraded(true);
1090 extension->set_being_upgraded(true);
1091 }
[email protected]0c6da502009-08-14 22:32:391092
[email protected]ca3dbf52010-05-19 22:27:061093 // To upgrade an extension in place, unload the old one and
1094 // then load the new one.
1095 UnloadExtension(old->id());
1096 old = NULL;
1097
1098 if (!allow_silent_upgrade) {
1099 // Extension has changed permissions significantly. Disable it. We
1100 // send a notification below.
1101 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
1102 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]0c6da502009-08-14 22:32:391103 }
[email protected]ba74f352009-06-11 18:54:451104 }
[email protected]86a274072009-06-11 02:06:451105
[email protected]ae09ca62009-08-21 19:46:461106 switch (extension_prefs_->GetExtensionState(extension->id())) {
1107 case Extension::ENABLED:
1108 extensions_.push_back(scoped_extension.release());
1109
[email protected]62d30f42009-10-01 22:36:061110 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:461111
[email protected]e8c729a2010-03-09 19:55:191112 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
1113 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:461114 break;
1115 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:451116 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:401117 NotificationService::current()->Notify(
1118 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:341119 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401120 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:461121 break;
1122 default:
[email protected]d11c8e92009-10-20 23:26:401123 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:461124 break;
[email protected]811f3432009-07-25 19:38:211125 }
[email protected]e72e8eb82009-06-18 17:21:511126 }
[email protected]aab98a52009-12-02 03:22:351127
[email protected]1e8c93f2010-02-08 22:58:311128 extension->set_being_upgraded(false);
1129
[email protected]aab98a52009-12-02 03:22:351130 UpdateActiveExtensionsInCrashReporter();
[email protected]0b004da2010-07-02 17:54:311131
1132 if (profile_->GetTemplateURLModel())
1133 profile_->GetTemplateURLModel()->RegisterExtensionKeyword(extension);
[email protected]b671760b2010-07-15 21:13:471134
1135 // Load the icon for omnibox-enabled extensions so it will be ready to display
1136 // in the URL bar.
[email protected]29d0d4ac2010-09-08 21:10:311137 if (!extension->omnibox_keyword().empty()) {
1138 omnibox_popup_icon_manager_.LoadIcon(extension);
[email protected]b671760b2010-07-15 21:13:471139 omnibox_icon_manager_.LoadIcon(extension);
[email protected]29d0d4ac2010-09-08 21:10:311140 }
[email protected]aab98a52009-12-02 03:22:351141}
1142
1143void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:391144 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:351145 for (size_t i = 0; i < extensions_.size(); ++i) {
[email protected]3ba0fd32010-06-19 05:39:101146 if (!extensions_[i]->is_theme())
[email protected]c8865962009-12-16 07:47:391147 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:351148 }
1149
1150 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:251151}
1152
[email protected]2a409532009-08-28 19:39:441153void ExtensionsService::OnExtensionInstalled(Extension* extension,
1154 bool allow_privilege_increase) {
[email protected]4416c5a2010-06-26 01:28:571155 // Ensure extension is deleted unless we transfer ownership.
1156 scoped_ptr<Extension> scoped_extension(extension);
1157 Extension::State initial_state = Extension::DISABLED;
1158 bool initial_enable_incognito = false;
[email protected]aa142702010-03-26 01:26:331159 PendingExtensionMap::iterator it =
1160 pending_extensions_.find(extension->id());
[email protected]4416c5a2010-06-26 01:28:571161 if (it != pending_extensions_.end()) {
[email protected]11edd1e2010-07-21 00:14:501162 PendingExtensionInfo pending_extension_info = it->second;
[email protected]8ef78fd2010-08-19 17:14:321163 PendingExtensionInfo::ExpectedCrxType expected_crx_type =
1164 pending_extension_info.expected_crx_type;
1165 bool is_from_sync = pending_extension_info.is_from_sync;
[email protected]11edd1e2010-07-21 00:14:501166 pending_extensions_.erase(it);
1167 it = pending_extensions_.end();
[email protected]8ef78fd2010-08-19 17:14:321168
[email protected]4416c5a2010-06-26 01:28:571169 // Set initial state from pending extension data.
[email protected]8ef78fd2010-08-19 17:14:321170 PendingExtensionInfo::ExpectedCrxType actual_crx_type =
1171 (extension->is_theme() ? PendingExtensionInfo::THEME
1172 : PendingExtensionInfo::EXTENSION);
1173
1174 if (expected_crx_type != PendingExtensionInfo::UNKNOWN &&
1175 expected_crx_type != actual_crx_type) {
[email protected]4416c5a2010-06-26 01:28:571176 LOG(WARNING)
1177 << "Not installing pending extension " << extension->id()
[email protected]8ef78fd2010-08-19 17:14:321178 << " with is_theme = " << extension->is_theme();
[email protected]4416c5a2010-06-26 01:28:571179 // Delete the extension directory since we're not going to
1180 // load it.
1181 ChromeThread::PostTask(
1182 ChromeThread::FILE, FROM_HERE,
1183 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1184 return;
1185 }
[email protected]8ef78fd2010-08-19 17:14:321186
1187 // If |extension| is not syncable, and was installed via sync, disallow
1188 // the instanation.
1189 //
1190 // Themes are always allowed. Because they contain no active code, they
1191 // are less of a risk than extensions.
1192 //
1193 // If |is_from_sync| is false, then the install was not initiated by sync,
1194 // and this check should pass. Extensions that were installed from an
1195 // update URL in external_extensions.json are an example. They are not
1196 // syncable, because the user did not make an explicit choice to install
1197 // them. However, they were installed through the update mechanism, so
1198 // control must pass into this function.
1199 //
1200 // TODO(akalin): When we do apps sync, we have to work with its
1201 // traits, too.
[email protected]2a3e22b12010-08-13 04:55:171202 const browser_sync::ExtensionSyncTraits extension_sync_traits =
1203 browser_sync::GetExtensionSyncTraits();
[email protected]06e33202010-08-16 23:45:151204 const browser_sync::ExtensionSyncTraits app_sync_traits =
1205 browser_sync::GetAppSyncTraits();
[email protected]2a3e22b12010-08-13 04:55:171206 // If an extension is a theme, we bypass the valid/syncable check
1207 // as themes are harmless.
[email protected]8ef78fd2010-08-19 17:14:321208 if (!extension->is_theme() && is_from_sync &&
[email protected]2a3e22b12010-08-13 04:55:171209 !browser_sync::IsExtensionValidAndSyncable(
[email protected]06e33202010-08-16 23:45:151210 *extension, extension_sync_traits.allowed_extension_types) &&
1211 !browser_sync::IsExtensionValidAndSyncable(
1212 *extension, app_sync_traits.allowed_extension_types)) {
[email protected]11edd1e2010-07-21 00:14:501213 // We're an extension installed via sync that is unsyncable,
1214 // i.e. we may have been syncable previously. We block these
1215 // installs. We'll have to update the clause above if we decide
1216 // to sync other extension-like things, like apps or user
1217 // scripts.
1218 //
1219 // Note that this creates a small window where a user who tries
1220 // to download/install an extension that is simultaneously
1221 // installed via sync (and blocked) will find his download
1222 // blocked.
1223 //
1224 // TODO(akalin): Remove this check once we've put in UI to
1225 // approve synced extensions.
1226 LOG(WARNING)
[email protected]2a3e22b12010-08-13 04:55:171227 << "Not installing invalid or unsyncable extension "
1228 << extension->id();
[email protected]11edd1e2010-07-21 00:14:501229 // Delete the extension directory since we're not going to
1230 // load it.
1231 ChromeThread::PostTask(
1232 ChromeThread::FILE, FROM_HERE,
1233 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
1234 return;
1235 }
[email protected]8ef78fd2010-08-19 17:14:321236 if (extension->is_theme()) {
[email protected]11edd1e2010-07-21 00:14:501237 DCHECK(pending_extension_info.enable_on_install);
[email protected]4416c5a2010-06-26 01:28:571238 initial_state = Extension::ENABLED;
[email protected]11edd1e2010-07-21 00:14:501239 DCHECK(!pending_extension_info.enable_incognito_on_install);
[email protected]4416c5a2010-06-26 01:28:571240 initial_enable_incognito = false;
1241 } else {
1242 initial_state =
[email protected]11edd1e2010-07-21 00:14:501243 pending_extension_info.enable_on_install ?
[email protected]4416c5a2010-06-26 01:28:571244 Extension::ENABLED : Extension::DISABLED;
1245 initial_enable_incognito =
[email protected]11edd1e2010-07-21 00:14:501246 pending_extension_info.enable_incognito_on_install;
[email protected]4416c5a2010-06-26 01:28:571247 }
[email protected]4416c5a2010-06-26 01:28:571248 } else {
[email protected]dbec3792010-08-10 00:08:451249 // Make sure we preserve enabled/disabled states.
[email protected]4416c5a2010-06-26 01:28:571250 Extension::State existing_state =
1251 extension_prefs_->GetExtensionState(extension->id());
1252 initial_state =
1253 (existing_state == Extension::DISABLED) ?
1254 Extension::DISABLED : Extension::ENABLED;
[email protected]dbec3792010-08-10 00:08:451255 initial_enable_incognito =
1256 extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]aa142702010-03-26 01:26:331257 }
1258
[email protected]4416c5a2010-06-26 01:28:571259 extension_prefs_->OnExtensionInstalled(
1260 extension, initial_state, initial_enable_incognito);
[email protected]25b34332009-06-05 21:53:191261
[email protected]92a5b1d2010-07-20 00:42:001262 // Unpacked extensions start off with file access since they are a developer
1263 // feature.
1264 if (extension->location() == Extension::LOAD)
1265 extension_prefs_->SetAllowFileAccess(extension->id(), true);
1266
[email protected]4a190632009-05-09 01:07:421267 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
1268 // to apply it.
[email protected]3ba0fd32010-06-19 05:39:101269 if (extension->is_theme()) {
[email protected]9ceb07342009-07-26 04:09:231270 NotificationService::current()->Notify(
1271 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341272 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:231273 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:271274 } else {
1275 NotificationService::current()->Notify(
1276 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:341277 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:271278 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:421279 }
[email protected]7577a5c52009-07-30 06:21:581280
[email protected]4416c5a2010-06-26 01:28:571281 // Transfer ownership of |extension| to OnExtensionLoaded.
1282 OnExtensionLoaded(scoped_extension.release(), allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:421283}
1284
[email protected]0c6da502009-08-14 22:32:391285Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
1286 bool include_enabled,
1287 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:051288 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:391289 if (include_enabled) {
1290 for (ExtensionList::const_iterator iter = extensions_.begin();
1291 iter != extensions_.end(); ++iter) {
1292 if ((*iter)->id() == lowercase_id)
1293 return *iter;
1294 }
1295 }
1296 if (include_disabled) {
1297 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
1298 iter != disabled_extensions_.end(); ++iter) {
1299 if ((*iter)->id() == lowercase_id)
1300 return *iter;
1301 }
[email protected]ce5c4502009-05-06 16:46:111302 }
1303 return NULL;
1304}
1305
[email protected]9f1087e2009-06-15 17:29:321306Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
[email protected]a888b29e62010-04-01 13:38:571307 return url.scheme() != chrome::kExtensionScheme ? NULL :
1308 GetExtensionById(url.host(), false);
1309}
1310
1311Extension* ExtensionsService::GetExtensionByWebExtent(const GURL& url) {
1312 for (size_t i = 0; i < extensions_.size(); ++i) {
1313 if (extensions_[i]->web_extent().ContainsURL(url))
1314 return extensions_[i];
1315 }
1316 return NULL;
[email protected]9f1087e2009-06-15 17:29:321317}
1318
[email protected]583d45c12010-08-31 02:48:121319bool ExtensionsService::ExtensionBindingsAllowed(const GURL& url) {
1320 // Allow bindings for all packaged extension.
1321 if (GetExtensionByURL(url))
1322 return true;
1323
1324 // Allow bindings for all component, hosted apps.
1325 Extension* extension = GetExtensionByWebExtent(url);
1326 return (extension && extension->location() == Extension::COMPONENT);
1327}
1328
[email protected]6d2e60bd2010-06-03 22:37:391329Extension* ExtensionsService::GetExtensionByOverlappingWebExtent(
[email protected]9f72aa02010-06-25 10:01:051330 const ExtensionExtent& extent) {
[email protected]22c966c2010-06-26 06:35:021331 for (size_t i = 0; i < extensions_.size(); ++i) {
1332 if (extensions_[i]->web_extent().OverlapsWith(extent))
1333 return extensions_[i];
1334 }
1335
[email protected]6d2e60bd2010-06-03 22:37:391336 return NULL;
1337}
1338
[email protected]b671760b2010-07-15 21:13:471339const SkBitmap& ExtensionsService::GetOmniboxIcon(
1340 const std::string& extension_id) {
1341 return omnibox_icon_manager_.GetIcon(extension_id);
1342}
1343
[email protected]29d0d4ac2010-09-08 21:10:311344const SkBitmap& ExtensionsService::GetOmniboxPopupIcon(
1345 const std::string& extension_id) {
1346 return omnibox_popup_icon_manager_.GetIcon(extension_id);
1347}
1348
[email protected]a1257b12009-06-12 02:51:341349void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:061350 ChromeThread::PostTask(
1351 ChromeThread::FILE, FROM_HERE,
1352 NewRunnableMethod(
1353 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:341354}
1355
1356void ExtensionsService::SetProviderForTesting(
1357 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:061358 ChromeThread::PostTask(
1359 ChromeThread::FILE, FROM_HERE,
1360 NewRunnableMethod(
1361 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
1362 location, test_provider));
[email protected]a1257b12009-06-12 02:51:341363}
1364
[email protected]8ef78fd2010-08-19 17:14:321365void ExtensionsService::OnExternalExtensionFileFound(
1366 const std::string& id,
1367 const std::string& version,
1368 const FilePath& path,
1369 Extension::Location location) {
[email protected]7577a5c52009-07-30 06:21:581370 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:491371 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:581372 // installed on every startup.
[email protected]61b411612009-11-10 23:17:411373 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:111374 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:581375 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:111376 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:581377 case -1: // existing version is older, we should upgrade
1378 break;
1379 case 0: // existing version is same, do nothing
1380 return;
1381 case 1: // existing version is newer, uh-oh
1382 LOG(WARNING) << "Found external version of extension " << id
1383 << "that is older than current version. Current version "
1384 << "is: " << existing->VersionString() << ". New version "
1385 << "is: " << version << ". Keeping current version.";
1386 return;
1387 }
1388 }
1389
[email protected]6dfbbf82010-03-12 23:09:161390 scoped_refptr<CrxInstaller> installer(
1391 new CrxInstaller(install_directory_,
1392 this, // frontend
1393 NULL)); // no client (silent install)
1394 installer->set_install_source(location);
1395 installer->set_expected_id(id);
1396 installer->set_allow_privilege_increase(true);
1397 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581398}
1399
[email protected]d11c8e92009-10-20 23:26:401400void ExtensionsService::ReportExtensionLoadError(
1401 const FilePath& extension_path,
1402 const std::string &error,
1403 NotificationType type,
1404 bool be_noisy) {
1405 NotificationService* service = NotificationService::current();
1406 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341407 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401408 Details<const std::string>(&error));
1409
1410 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161411 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:401412 std::string message = StringPrintf("Could not load extension from '%s'. %s",
1413 path_str.c_str(), error.c_str());
1414 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1415}
1416
[email protected]4814b512009-11-07 00:12:291417void ExtensionsService::Observe(NotificationType type,
1418 const NotificationSource& source,
1419 const NotificationDetails& details) {
1420 switch (type.value) {
1421 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
1422 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1423 OrphanedDevTools::iterator iter =
1424 orphaned_dev_tools_.find(host->extension()->id());
1425 if (iter == orphaned_dev_tools_.end())
1426 return;
1427
1428 DevToolsManager::GetInstance()->AttachClientHost(
1429 iter->second, host->render_view_host());
1430 orphaned_dev_tools_.erase(iter);
1431 break;
1432 }
1433
[email protected]a4ed6282009-12-14 20:51:161434 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]bc535ee52010-08-31 18:40:321435 if (profile_ != Source<Profile>(source).ptr()->GetOriginalProfile())
1436 break;
[email protected]a4ed6282009-12-14 20:51:161437
[email protected]f128af42010-08-05 18:05:261438 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1439
1440 // TODO(rafaelw): Remove this check and ExtensionHost::recently_deleted().
1441 // This is only here to help track down crbug.com/49114.
1442 ExtensionHost::HostPointerList::iterator iter =
1443 ExtensionHost::recently_deleted()->begin();
1444 for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
1445 if (*iter == host) {
1446 CHECK(host->GetURL().spec().size() + 2 != 0);
1447 break;
1448 }
1449 }
1450 if (iter == ExtensionHost::recently_deleted()->end())
1451 CHECK(host->GetURL().spec().size() + 1 != 0);
1452
[email protected]31f77262009-12-02 20:48:531453 // Unload the entire extension. We want it to be in a consistent state:
1454 // either fully working or not loaded at all, but never half-crashed.
[email protected]bc535ee52010-08-31 18:40:321455 // We do it in a PostTask so that other handlers of this notification will
1456 // still have access to the Extension and ExtensionHost.
1457 MessageLoop::current()->PostTask(FROM_HERE,
1458 NewRunnableMethod(this, &ExtensionsService::UnloadExtension,
1459 host->extension()->id()));
[email protected]31f77262009-12-02 20:48:531460 break;
1461 }
1462
[email protected]aa96d3a2010-08-21 08:45:251463 case NotificationType::PREF_CHANGED: {
1464 std::string* pref_name = Details<std::string>(details).ptr();
1465 DCHECK(*pref_name == prefs::kExtensionInstallAllowList ||
1466 *pref_name == prefs::kExtensionInstallDenyList);
1467 CheckAdminBlacklist();
1468 break;
1469 }
1470
[email protected]4814b512009-11-07 00:12:291471 default:
1472 NOTREACHED() << "Unexpected notification type.";
1473 }
1474}
1475
[email protected]377011d2010-07-20 04:18:501476bool ExtensionsService::HasApps() {
1477 if (!extensions_enabled_)
1478 return false;
1479
1480 for (ExtensionList::const_iterator it = extensions_.begin();
1481 it != extensions_.end(); ++it) {
1482 if ((*it)->is_app())
1483 return true;
1484 }
1485
1486 return false;
1487}
[email protected]4814b512009-11-07 00:12:291488
[email protected]6014d672008-12-05 00:38:251489// ExtensionsServicesBackend
1490
[email protected]894bb502009-05-21 22:39:571491ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:061492 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:081493 : frontend_(NULL),
1494 install_directory_(install_directory),
[email protected]8ef78fd2010-08-19 17:14:321495 alert_on_error_(false),
1496 external_extension_added_(false) {
[email protected]7577a5c52009-07-30 06:21:581497 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
1498 // pref data in the ctor and that is called on the UI thread. Would be better
1499 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:341500 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:011501 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:151502 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341503#if defined(OS_WIN)
1504 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:011505 linked_ptr<ExternalExtensionProvider>(
1506 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341507#endif
1508}
1509
1510ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:571511}
1512
[email protected]b0beaa662009-02-26 00:04:151513void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:571514 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151515 frontend_ = frontend;
1516
1517 // Explicit UI loads are always noisy.
1518 alert_on_error_ = true;
1519
[email protected]cc5da332009-03-04 08:02:511520 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:501521 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:451522
1523 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:161524 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:451525
[email protected]ab6f2b22009-07-28 23:28:371526 std::string error;
1527 Extension* extension = extension_file_util::LoadExtension(
1528 extension_path,
1529 false, // Don't require id
1530 &error);
1531
1532 if (!extension) {
1533 ReportExtensionLoadError(extension_path, error);
1534 return;
[email protected]0877fd92009-02-03 16:34:061535 }
[email protected]ab6f2b22009-07-28 23:28:371536
1537 extension->set_location(Extension::LOAD);
[email protected]e8c729a2010-03-09 19:55:191538
1539 // Report this as an installed extension so that it gets remembered in the
1540 // prefs.
1541 ChromeThread::PostTask(
1542 ChromeThread::UI, FROM_HERE,
1543 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
1544 extension, true));
[email protected]0877fd92009-02-03 16:34:061545}
1546
[email protected]6014d672008-12-05 00:38:251547void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:511548 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:061549 ChromeThread::PostTask(
1550 ChromeThread::UI, FROM_HERE,
1551 NewRunnableMethod(
1552 frontend_,
[email protected]d11c8e92009-10-20 23:26:401553 &ExtensionsService::ReportExtensionLoadError, extension_path,
1554 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:251555}
1556
[email protected]a1257b12009-06-12 02:51:341557bool ExtensionsServiceBackend::LookupExternalExtension(
1558 const std::string& id, Version** version, Extension::Location* location) {
1559 scoped_ptr<Version> extension_version;
1560 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1561 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011562 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341563 extension_version.reset(provider->RegisteredVersion(id, location));
1564 if (extension_version.get()) {
1565 if (version)
1566 *version = extension_version.release();
1567 return true;
1568 }
1569 }
1570 return false;
1571}
1572
[email protected]b0beaa662009-02-26 00:04:151573// Some extensions will autoupdate themselves externally from Chrome. These
1574// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191575// these, the extension will register its location in the the preferences file
1576// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151577// check that location for a .crx file, which it will then install locally if
1578// a new version is available.
1579void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571580 std::set<std::string> ids_to_ignore,
1581 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151582 // Note that this installation is intentionally silent (since it didn't
1583 // go through the front-end). Extensions that are registered in this
1584 // way are effectively considered 'pre-bundled', and so implicitly
1585 // trusted. In general, if something has HKLM or filesystem access,
1586 // they could install an extension manually themselves anyway.
1587 alert_on_error_ = false;
1588 frontend_ = frontend;
[email protected]8ef78fd2010-08-19 17:14:321589 external_extension_added_ = false;
[email protected]b0beaa662009-02-26 00:04:151590
[email protected]a1257b12009-06-12 02:51:341591 // Ask each external extension provider to give us a call back for each
[email protected]8ef78fd2010-08-19 17:14:321592 // extension they know about. See OnExternalExtension(File|UpdateUrl)Found.
1593
[email protected]a1257b12009-06-12 02:51:341594 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1595 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011596 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341597 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191598 }
[email protected]8ef78fd2010-08-19 17:14:321599
1600 if (external_extension_added_ && frontend->updater()) {
1601 ChromeThread::PostTask(
1602 ChromeThread::UI, FROM_HERE,
1603 NewRunnableMethod(
1604 frontend->updater(), &ExtensionUpdater::CheckNow));
1605 }
[email protected]b0beaa662009-02-26 00:04:151606}
1607
[email protected]ae09ca62009-08-21 19:46:461608void ExtensionsServiceBackend::CheckExternalUninstall(
1609 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1610 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341611 // Check if the providers know about this extension.
1612 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461613 if (i == external_extension_providers_.end()) {
1614 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1615 << location;
1616 return;
[email protected]b0beaa662009-02-26 00:04:151617 }
[email protected]25b34332009-06-05 21:53:191618
[email protected]ae09ca62009-08-21 19:46:461619 scoped_ptr<Version> version;
1620 version.reset(i->second->RegisteredVersion(id, NULL));
1621 if (version.get())
1622 return; // Yup, known extension, don't uninstall.
1623
1624 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061625 ChromeThread::PostTask(
1626 ChromeThread::UI, FROM_HERE,
1627 NewRunnableMethod(
1628 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151629}
1630
[email protected]a1257b12009-06-12 02:51:341631void ExtensionsServiceBackend::ClearProvidersForTesting() {
1632 external_extension_providers_.clear();
1633}
1634
1635void ExtensionsServiceBackend::SetProviderForTesting(
1636 Extension::Location location,
1637 ExternalExtensionProvider* test_provider) {
1638 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011639 external_extension_providers_[location] =
1640 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341641}
1642
[email protected]8ef78fd2010-08-19 17:14:321643void ExtensionsServiceBackend::OnExternalExtensionFileFound(
[email protected]7577a5c52009-07-30 06:21:581644 const std::string& id, const Version* version, const FilePath& path,
1645 Extension::Location location) {
[email protected]9e54cb572010-09-03 20:08:061646 DCHECK(version);
[email protected]95d29192009-10-30 01:49:061647 ChromeThread::PostTask(
1648 ChromeThread::UI, FROM_HERE,
1649 NewRunnableMethod(
[email protected]8ef78fd2010-08-19 17:14:321650 frontend_, &ExtensionsService::OnExternalExtensionFileFound, id,
[email protected]95d29192009-10-30 01:49:061651 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191652}
[email protected]c6d474f82009-12-16 21:11:061653
[email protected]8ef78fd2010-08-19 17:14:321654void ExtensionsServiceBackend::OnExternalExtensionUpdateUrlFound(
1655 const std::string& id, const GURL& update_url) {
1656 if (frontend_->GetExtensionById(id, true)) {
1657 // Already installed. Do not change the update URL that the extension set.
1658 return;
1659 }
1660
1661 frontend_->AddPendingExtensionFromExternalUpdateUrl(id, update_url);
1662 external_extension_added_ |= true;
1663}
1664
[email protected]2111b1a2010-03-12 18:12:441665void ExtensionsServiceBackend::ReloadExtensionManifests(
[email protected]c6d474f82009-12-16 21:11:061666 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1667 base::TimeTicks start_time,
1668 scoped_refptr<ExtensionsService> frontend) {
1669 frontend_ = frontend;
1670
1671 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1672 ExtensionInfo* info = extensions_to_reload->at(i).get();
[email protected]2111b1a2010-03-12 18:12:441673 if (!ShouldReloadExtensionManifest(*info))
[email protected]c6d474f82009-12-16 21:11:061674 continue;
1675
1676 // We need to reload original manifest in order to localize properly.
1677 std::string error;
1678 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1679 info->extension_path, false, &error));
1680
1681 if (extension.get())
1682 extensions_to_reload->at(i)->extension_manifest.reset(
1683 static_cast<DictionaryValue*>(
1684 extension->manifest_value()->DeepCopy()));
1685 }
1686
1687 // Finish installing on UI thread.
1688 ChromeThread::PostTask(
1689 ChromeThread::UI, FROM_HERE,
1690 NewRunnableMethod(
1691 frontend_,
1692 &ExtensionsService::ContinueLoadAllExtensions,
1693 extensions_to_reload,
1694 start_time,
1695 true));
1696}