blob: 17fd3903c64db8e7336ba43efee6d9ea7bdcb59c [file] [log] [blame]
[email protected]ce5c4502009-05-06 16:46:111// Copyright (c) 2009 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]e2eb43112009-05-29 21:19:547#include "app/l10n_util.h"
8#include "base/command_line.h"
[email protected]fbcc40302009-06-12 20:45:459#include "base/crypto/signature_verifier.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/file_util.h"
[email protected]902f7cd2009-05-22 19:02:1911#include "base/gfx/png_encoder.h"
[email protected]cc655912009-01-29 23:19:1912#include "base/scoped_handle.h"
13#include "base/scoped_temp_dir.h"
[email protected]5c238752009-06-13 10:29:0714#include "base/stl_util-inl.h"
[email protected]6014d672008-12-05 00:38:2515#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1916#include "base/third_party/nss/blapi.h"
17#include "base/third_party/nss/sha256.h"
[email protected]6014d672008-12-05 00:38:2518#include "base/thread.h"
[email protected]cc655912009-01-29 23:19:1919#include "base/values.h"
20#include "net/base/file_stream.h"
[email protected]a57209872009-05-04 22:53:1421#include "chrome/browser/browser.h"
22#include "chrome/browser/browser_list.h"
[email protected]6014d672008-12-05 00:38:2523#include "chrome/browser/browser_process.h"
[email protected]1fca1492009-05-15 22:23:4324#include "chrome/browser/chrome_thread.h"
[email protected]fbcc40302009-06-12 20:45:4525#include "chrome/browser/extensions/extension_creator.h"
[email protected]b68d5ed2009-04-16 02:41:2826#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]481e1a42009-05-06 20:56:0527#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1728#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3429#include "chrome/browser/extensions/external_extension_provider.h"
30#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]81e63782009-02-27 19:35:0931#include "chrome/browser/profile.h"
[email protected]1fca1492009-05-15 22:23:4332#include "chrome/browser/utility_process_host.h"
[email protected]e2eb43112009-05-29 21:19:5433#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5834#include "chrome/common/extensions/extension.h"
[email protected]cb691e82009-07-13 14:59:0135#include "chrome/common/extensions/extension_constants.h"
[email protected]5b1a0e22009-05-26 19:00:5836#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]1fca1492009-05-15 22:23:4337#include "chrome/common/extensions/extension_unpacker.h"
[email protected]6014d672008-12-05 00:38:2538#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2639#include "chrome/common/notification_service.h"
[email protected]25b34332009-06-05 21:53:1940#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5741#include "chrome/common/pref_service.h"
[email protected]4777bc562009-06-01 02:53:0042#include "chrome/common/zip.h"
[email protected]a57209872009-05-04 22:53:1443#include "chrome/common/url_constants.h"
[email protected]e2eb43112009-05-29 21:19:5444#include "grit/chromium_strings.h"
45#include "grit/generated_resources.h"
[email protected]fbcc40302009-06-12 20:45:4546#include "net/base/base64.h"
[email protected]902f7cd2009-05-22 19:02:1947#include "third_party/skia/include/core/SkBitmap.h"
[email protected]c64631652009-04-29 22:24:3148
[email protected]79db6232009-02-13 20:51:2049#if defined(OS_WIN)
[email protected]e2eb43112009-05-29 21:19:5450#include "app/win_util.h"
[email protected]a1257b12009-06-12 02:51:3451#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]638d3522009-06-25 14:52:0152#elif defined(OS_MACOSX)
53#include "base/scoped_cftyperef.h"
54#include "base/sys_string_conversions.h"
55#include <CoreFoundation/CFUserNotification.h>
[email protected]79db6232009-02-13 20:51:2056#endif
[email protected]6014d672008-12-05 00:38:2557
[email protected]25b34332009-06-05 21:53:1958// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2559
[email protected]fbcc40302009-06-12 20:45:4560const char ExtensionsService::kExtensionHeaderMagic[] = "Cr24";
61
[email protected]cc655912009-01-29 23:19:1962const char* ExtensionsService::kInstallDirectoryName = "Extensions";
63const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
64const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
[email protected]b0beaa662009-02-26 00:04:1565
66namespace {
[email protected]6014d672008-12-05 00:38:2567
[email protected]1fca1492009-05-15 22:23:4368// A temporary subdirectory where we unpack extensions.
69const char* kUnpackExtensionDir = "TEMP_UNPACK";
70
[email protected]fbcc40302009-06-12 20:45:4571// Unpacking errors
72const char* kBadMagicNumberError = "Bad magic number";
73const char* kBadHeaderSizeError = "Excessively large key or signature";
74const char* kBadVersionNumberError = "Bad version number";
75const char* kInvalidExtensionHeaderError = "Invalid extension header";
76const char* kInvalidPublicKeyError = "Invalid public key";
77const char* kInvalidSignatureError = "Invalid signature";
78const char* kSignatureVerificationFailed = "Signature verification failed";
79const char* kSignatureVerificationInitFailed =
80 "Signature verification initialization failed. This is most likely "
81 "caused by a public key in the wrong format (should encode algorithm).";
[email protected]b0beaa662009-02-26 00:04:1582}
83
[email protected]1fca1492009-05-15 22:23:4384// This class coordinates an extension unpack task which is run in a separate
85// process. Results are sent back to this class, which we route to the
86// ExtensionServiceBackend.
87class ExtensionsServiceBackend::UnpackerClient
88 : public UtilityProcessHost::Client {
89 public:
90 UnpackerClient(ExtensionsServiceBackend* backend,
91 const FilePath& extension_path,
[email protected]fbcc40302009-06-12 20:45:4592 const std::string& public_key,
[email protected]93fd78f42009-07-10 16:43:1793 const std::string& expected_id,
94 bool silent)
[email protected]1fca1492009-05-15 22:23:4395 : backend_(backend), extension_path_(extension_path),
[email protected]93fd78f42009-07-10 16:43:1796 public_key_(public_key), expected_id_(expected_id), got_response_(false),
97 silent_(silent) {
[email protected]1fca1492009-05-15 22:23:4398 }
99
100 // Starts the unpack task. We call back to the backend when the task is done,
101 // or a problem occurs.
102 void Start() {
103 AddRef(); // balanced in OnUnpackExtensionReply()
104
105 // TODO(mpcomplete): handle multiple installs
106 FilePath temp_dir = backend_->install_directory_.AppendASCII(
107 kUnpackExtensionDir);
108 if (!file_util::CreateDirectory(temp_dir)) {
109 backend_->ReportExtensionInstallError(extension_path_,
110 "Failed to create temporary directory.");
111 return;
112 }
113
114 temp_extension_path_ = temp_dir.Append(extension_path_.BaseName());
115 if (!file_util::CopyFile(extension_path_, temp_extension_path_)) {
116 backend_->ReportExtensionInstallError(extension_path_,
117 "Failed to copy extension file to temporary directory.");
118 return;
119 }
120
121 if (backend_->resource_dispatcher_host_) {
122 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE,
123 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
124 backend_->resource_dispatcher_host_,
125 MessageLoop::current()));
126 } else {
127 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
128 // in a unit test and run the unpacker directly in-process.
129 ExtensionUnpacker unpacker(temp_extension_path_);
[email protected]902f7cd2009-05-22 19:02:19130 if (unpacker.Run()) {
[email protected]facd7a7652009-06-05 23:15:02131 OnUnpackExtensionSucceededImpl(*unpacker.parsed_manifest(),
132 unpacker.decoded_images());
[email protected]902f7cd2009-05-22 19:02:19133 } else {
134 OnUnpackExtensionFailed(unpacker.error_message());
135 }
[email protected]1fca1492009-05-15 22:23:43136 }
137 }
138
139 private:
140 // UtilityProcessHost::Client
141 virtual void OnProcessCrashed() {
[email protected]2b4053c2009-05-29 20:28:09142 // Don't report crashes if they happen after we got a response.
143 if (got_response_)
144 return;
145
146 OnUnpackExtensionFailed("Chrome crashed while trying to install.");
[email protected]1fca1492009-05-15 22:23:43147 }
148
[email protected]facd7a7652009-06-05 23:15:02149 virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest) {
150 ExtensionUnpacker::DecodedImages images;
151 if (!ExtensionUnpacker::ReadImagesFromFile(temp_extension_path_,
152 &images)) {
153 OnUnpackExtensionFailed("Couldn't read image data from disk.");
154 } else {
155 OnUnpackExtensionSucceededImpl(manifest, images);
156 }
157 }
158
159 void OnUnpackExtensionSucceededImpl(
[email protected]902f7cd2009-05-22 19:02:19160 const DictionaryValue& manifest,
[email protected]facd7a7652009-06-05 23:15:02161 const ExtensionUnpacker::DecodedImages& images) {
[email protected]fbcc40302009-06-12 20:45:45162 // Add our public key into the parsed manifest. We want it to be saved so
163 // that we can later refer to it (eg for generating ids, validating
164 // signatures, etc).
165 // The const_cast is hacky, but seems like the right thing here, rather than
166 // making a full copy just to make this change.
167 const_cast<DictionaryValue*>(&manifest)->SetString(
[email protected]cb691e82009-07-13 14:59:01168 extension_manifest_keys::kPublicKey, public_key_);
[email protected]fbcc40302009-06-12 20:45:45169
[email protected]902f7cd2009-05-22 19:02:19170 // The extension was unpacked to the temp dir inside our unpacking dir.
171 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
172 ExtensionsServiceBackend::kTempExtensionName);
173 backend_->OnExtensionUnpacked(extension_path_, extension_dir,
[email protected]93fd78f42009-07-10 16:43:17174 expected_id_, manifest, images, silent_);
[email protected]1fca1492009-05-15 22:23:43175 Cleanup();
[email protected]902f7cd2009-05-22 19:02:19176 }
177
178 virtual void OnUnpackExtensionFailed(const std::string& error_message) {
179 backend_->ReportExtensionInstallError(extension_path_, error_message);
180 Cleanup();
[email protected]1fca1492009-05-15 22:23:43181 }
182
183 // Cleans up our temp directory.
184 void Cleanup() {
[email protected]2b4053c2009-05-29 20:28:09185 if (got_response_)
186 return;
187
188 got_response_ = true;
[email protected]1fca1492009-05-15 22:23:43189 file_util::Delete(temp_extension_path_.DirName(), true);
[email protected]902f7cd2009-05-22 19:02:19190 Release(); // balanced in Run()
[email protected]1fca1492009-05-15 22:23:43191 }
192
193 // Starts the utility process that unpacks our extension.
194 void StartProcessOnIOThread(ResourceDispatcherHost* rdh,
195 MessageLoop* file_loop) {
196 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop);
197 host->StartExtensionUnpacker(temp_extension_path_);
198 }
199
200 scoped_refptr<ExtensionsServiceBackend> backend_;
201
202 // The path to the crx file that we're installing.
203 FilePath extension_path_;
204
[email protected]fbcc40302009-06-12 20:45:45205 // The public key of the extension we're installing.
206 std::string public_key_;
207
[email protected]1fca1492009-05-15 22:23:43208 // The path to the copy of the crx file in the temporary directory where we're
209 // unpacking it.
210 FilePath temp_extension_path_;
211
212 // The ID we expect this extension to have, if any.
213 std::string expected_id_;
214
[email protected]2b4053c2009-05-29 20:28:09215 // True if we got a response from the utility process and have cleaned up
216 // already.
217 bool got_response_;
[email protected]93fd78f42009-07-10 16:43:17218
219 // True if the install should be done with no confirmation dialog.
220 bool silent_;
[email protected]1fca1492009-05-15 22:23:43221};
222
[email protected]81e63782009-02-27 19:35:09223ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08224 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23225 PrefService* prefs,
226 const FilePath& install_directory,
[email protected]894bb502009-05-21 22:39:57227 MessageLoop* frontend_loop,
[email protected]93fd78f42009-07-10 16:43:17228 MessageLoop* backend_loop,
229 bool autoupdate_enabled)
[email protected]a9b00ac2009-06-25 21:03:23230 : extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]894bb502009-05-21 22:39:57231 backend_loop_(backend_loop),
[email protected]a9b00ac2009-06-25 21:03:23232 install_directory_(install_directory),
[email protected]abe7a8942009-06-23 05:14:29233 extensions_enabled_(false),
[email protected]e81dba32009-06-19 20:19:13234 show_extensions_prompts_(true),
235 ready_(false) {
[email protected]36a784c2009-06-23 06:21:08236 // Figure out if extension installation should be enabled.
237 if (command_line->HasSwitch(switches::kEnableExtensions))
238 extensions_enabled_ = true;
239 else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions))
240 extensions_enabled_ = true;
241
[email protected]93fd78f42009-07-10 16:43:17242 // Set up the ExtensionUpdater
243 if (autoupdate_enabled) {
244 int update_frequency = kDefaultUpdateFrequencySeconds;
245 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
246 update_frequency = StringToInt(WideToASCII(command_line->GetSwitchValue(
247 switches::kExtensionsUpdateFrequency)));
248 }
249 updater_ = new ExtensionUpdater(this, update_frequency, backend_loop_);
250 }
251
[email protected]a1257b12009-06-12 02:51:34252 backend_ = new ExtensionsServiceBackend(
253 install_directory_, g_browser_process->resource_dispatcher_host(),
[email protected]27b985d2009-06-25 17:53:15254 frontend_loop, extensions_enabled());
[email protected]6014d672008-12-05 00:38:25255}
256
257ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32258 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17259 if (updater_.get()) {
260 updater_->Stop();
261 }
[email protected]6014d672008-12-05 00:38:25262}
263
[email protected]abe7a8942009-06-23 05:14:29264void ExtensionsService::SetExtensionsEnabled(bool enabled) {
265 extensions_enabled_ = true;
266 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
267 &ExtensionsServiceBackend::set_extensions_enabled, enabled));
268}
269
[email protected]9f1087e2009-06-15 17:29:32270void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29271 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17272 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32273
[email protected]b68d5ed2009-04-16 02:41:28274 // Start up the extension event routers.
275 ExtensionBrowserEventRouter::GetInstance()->Init();
276
[email protected]9f1087e2009-06-15 17:29:32277 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57278
[email protected]9f1087e2009-06-15 17:29:32279 // TODO(erikkay) this should probably be deferred to a future point
280 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17281 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57282
[email protected]9f1087e2009-06-15 17:29:32283 // TODO(erikkay) this should probably be deferred as well.
284 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25285}
286
[email protected]3cf4f0992009-02-03 23:00:30287void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57288 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
289 &ExtensionsServiceBackend::InstallExtension,
290 extension_path,
291 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30292}
293
[email protected]e957fe52009-06-23 16:51:05294void ExtensionsService::UpdateExtension(const std::string& id,
295 const FilePath& extension_path,
296 bool alert_on_error,
[email protected]d1ca0ed12009-07-01 18:24:32297 ExtensionInstallCallback* callback) {
[email protected]e957fe52009-06-23 16:51:05298 if (callback) {
299 if (install_callbacks_.find(extension_path) != install_callbacks_.end()) {
300 // We can't have multiple outstanding install requests for the same
301 // path, so immediately indicate error via the callback here.
302 LOG(WARNING) << "Dropping update request for '" <<
303 extension_path.value() << "' (already in progress)'";
304 callback->Run(extension_path, static_cast<Extension*>(NULL));
305 delete callback;
306 return;
307 }
[email protected]d1ca0ed12009-07-01 18:24:32308 install_callbacks_[extension_path] =
309 linked_ptr<ExtensionInstallCallback>(callback);
[email protected]e957fe52009-06-23 16:51:05310 }
311
312 if (!GetExtensionById(id)) {
313 LOG(WARNING) << "Will not update extension " << id << " because it is not "
314 << "installed";
315 FireInstallCallback(extension_path, NULL);
316 return;
317 }
318
319 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
320 &ExtensionsServiceBackend::UpdateExtension, id, extension_path,
321 alert_on_error, scoped_refptr<ExtensionsService>(this)));
322}
323
[email protected]27b985d2009-06-25 17:53:15324void ExtensionsService::UninstallExtension(const std::string& extension_id,
325 bool external_uninstall) {
[email protected]9f1087e2009-06-15 17:29:32326 Extension* extension = GetExtensionById(extension_id);
[email protected]631cf822009-05-15 07:01:25327
[email protected]9f1087e2009-06-15 17:29:32328 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51329 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32330
[email protected]27b985d2009-06-25 17:53:15331 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32332
333 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51334 if (Extension::LOAD != extension->location()) {
[email protected]9f1087e2009-06-15 17:29:32335 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
336 &ExtensionsServiceBackend::UninstallExtension, extension_id));
337 }
338
339 UnloadExtension(extension_id);
340}
341
342void ExtensionsService::LoadExtension(const FilePath& extension_path) {
343 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
344 &ExtensionsServiceBackend::LoadSingleExtension,
345 extension_path, scoped_refptr<ExtensionsService>(this)));
346}
347
348void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51349 // Load the previously installed extensions.
350 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
351 &ExtensionsServiceBackend::LoadInstalledExtensions,
352 scoped_refptr<ExtensionsService>(this),
353 new InstalledExtensions(extension_prefs_.get())));
[email protected]9f1087e2009-06-15 17:29:32354}
355
[email protected]93fd78f42009-07-10 16:43:17356void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32357 // This installs or updates externally provided extensions.
358 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51359 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32360 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
361 &ExtensionsServiceBackend::CheckForExternalUpdates,
362 killed_extensions,
363 scoped_refptr<ExtensionsService>(this)));
364}
365
366void ExtensionsService::UnloadExtension(const std::string& extension_id) {
367 Extension* extension = NULL;
[email protected]631cf822009-05-15 07:01:25368 ExtensionList::iterator iter;
369 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
370 if ((*iter)->id() == extension_id) {
371 extension = *iter;
372 break;
373 }
374 }
375
[email protected]894bb502009-05-21 22:39:57376 // Callers should not send us nonexistant extensions.
377 CHECK(extension);
378
[email protected]631cf822009-05-15 07:01:25379 // Remove the extension from our list.
380 extensions_.erase(iter);
381
[email protected]631cf822009-05-15 07:01:25382 // Tell other services the extension is gone.
383 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
[email protected]c6e4a3412009-06-24 15:45:29384 Source<ExtensionsService>(this),
[email protected]631cf822009-05-15 07:01:25385 Details<Extension>(extension));
386
[email protected]631cf822009-05-15 07:01:25387 delete extension;
388}
389
[email protected]9f1087e2009-06-15 17:29:32390void ExtensionsService::UnloadAllExtensions() {
391 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29392 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32393 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32394 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29395
396 // TODO(erikkay) should there be a notification for this? We can't use
397 // EXTENSION_UNLOADED since that implies that the extension has been disabled
398 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32399}
400
401void ExtensionsService::ReloadExtensions() {
402 UnloadAllExtensions();
403 LoadAllExtensions();
404}
405
406void ExtensionsService::GarbageCollectExtensions() {
[email protected]894bb502009-05-21 22:39:57407 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
[email protected]9f1087e2009-06-15 17:29:32408 &ExtensionsServiceBackend::GarbageCollectExtensions,
409 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30410}
411
[email protected]e72e8eb82009-06-18 17:21:51412void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13413 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17414 if (updater_.get()) {
415 updater_->Start();
416 }
[email protected]e72e8eb82009-06-18 17:21:51417 NotificationService::current()->Notify(
418 NotificationType::EXTENSIONS_READY,
419 Source<ExtensionsService>(this),
420 NotificationService::NoDetails());
421}
422
[email protected]4a8d3272009-03-10 19:15:08423void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32424 scoped_ptr<ExtensionList> cleanup(new_extensions);
425
[email protected]abe7a8942009-06-23 05:14:29426 // Filter out any extensions that shouldn't be loaded. Themes are always
427 // loaded, but other extensions are only loaded if the extensions system is
428 // enabled.
[email protected]a1257b12009-06-12 02:51:34429 ExtensionList enabled_extensions;
[email protected]86a274072009-06-11 02:06:45430 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19431 iter != new_extensions->end(); ++iter) {
[email protected]abe7a8942009-06-23 05:14:29432 if (extensions_enabled() || (*iter)->IsTheme() ||
[email protected]919ddc82009-07-15 04:30:12433 (*iter)->location() == Extension::LOAD ||
[email protected]abe7a8942009-06-23 05:14:29434 (*iter)->location() == Extension::EXTERNAL_REGISTRY) {
[email protected]9f1087e2009-06-15 17:29:32435 Extension* old = GetExtensionById((*iter)->id());
436 if (old) {
437 if ((*iter)->version()->CompareTo(*(old->version())) > 0) {
438 // To upgrade an extension in place, unload the old one and
439 // then load the new one.
440 // TODO(erikkay) issue 12399
441 UnloadExtension(old->id());
442 } else {
443 // We already have the extension of the same or older version.
444 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
445 delete *iter;
446 continue;
447 }
448 }
[email protected]86a274072009-06-11 02:06:45449 enabled_extensions.push_back(*iter);
[email protected]9f1087e2009-06-15 17:29:32450 extensions_.push_back(*iter);
[email protected]ba74f352009-06-11 18:54:45451 } else {
452 // Extensions that get enabled get added to extensions_ and deleted later.
453 // Anything skipped must be deleted now so we don't leak.
454 delete *iter;
455 }
[email protected]86a274072009-06-11 02:06:45456 }
457
[email protected]e72e8eb82009-06-18 17:21:51458 if (enabled_extensions.size()) {
459 NotificationService::current()->Notify(
460 NotificationType::EXTENSIONS_LOADED,
[email protected]c6e4a3412009-06-24 15:45:29461 Source<ExtensionsService>(this),
[email protected]e72e8eb82009-06-18 17:21:51462 Details<ExtensionList>(&enabled_extensions));
463 }
[email protected]6014d672008-12-05 00:38:25464}
465
[email protected]e957fe52009-06-23 16:51:05466void ExtensionsService::OnExtensionInstalled(const FilePath& path,
467 Extension* extension, Extension::InstallType install_type) {
468 FireInstallCallback(path, extension);
[email protected]e72e8eb82009-06-18 17:21:51469 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19470
[email protected]4a190632009-05-09 01:07:42471 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
472 // to apply it.
473 if (extension->IsTheme()) {
474 NotificationService::current()->Notify(
475 NotificationType::THEME_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29476 Source<ExtensionsService>(this),
[email protected]4a190632009-05-09 01:07:42477 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27478 } else {
479 NotificationService::current()->Notify(
480 NotificationType::EXTENSION_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29481 Source<ExtensionsService>(this),
[email protected]9197f3b2009-06-02 00:49:27482 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42483 }
484}
485
[email protected]e957fe52009-06-23 16:51:05486
487void ExtensionsService::OnExtenionInstallError(const FilePath& path) {
488 FireInstallCallback(path, NULL);
489}
490
491void ExtensionsService::FireInstallCallback(const FilePath& path,
492 Extension* extension) {
493 CallbackMap::iterator iter = install_callbacks_.find(path);
494 if (iter != install_callbacks_.end()) {
495 iter->second->Run(path, extension);
496 install_callbacks_.erase(iter);
497 }
498}
499
500void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id,
501 const FilePath& path) {
502 FireInstallCallback(path, NULL);
[email protected]9f1087e2009-06-15 17:29:32503 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42504 if (extension && extension->IsTheme()) {
505 NotificationService::current()->Notify(
506 NotificationType::THEME_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29507 Source<ExtensionsService>(this),
[email protected]4a190632009-05-09 01:07:42508 Details<Extension>(extension));
509 }
[email protected]cc655912009-01-29 23:19:19510}
511
[email protected]9f1087e2009-06-15 17:29:32512Extension* ExtensionsService::GetExtensionById(const std::string& id) {
[email protected]e957fe52009-06-23 16:51:05513 std::string lowercase_id = StringToLowerASCII(id);
[email protected]ce5c4502009-05-06 16:46:11514 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42515 iter != extensions_.end(); ++iter) {
[email protected]e957fe52009-06-23 16:51:05516 if ((*iter)->id() == lowercase_id)
[email protected]ce5c4502009-05-06 16:46:11517 return *iter;
518 }
519 return NULL;
520}
521
[email protected]9f1087e2009-06-15 17:29:32522Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
523 std::string host = url.host();
524 return GetExtensionById(host);
525}
526
[email protected]a1257b12009-06-12 02:51:34527void ExtensionsService::ClearProvidersForTesting() {
528 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
529 &ExtensionsServiceBackend::ClearProvidersForTesting));
530}
531
532void ExtensionsService::SetProviderForTesting(
533 Extension::Location location, ExternalExtensionProvider* test_provider) {
534 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
535 &ExtensionsServiceBackend::SetProviderForTesting,
536 location, test_provider));
537}
538
[email protected]6014d672008-12-05 00:38:25539// ExtensionsServicesBackend
540
[email protected]894bb502009-05-21 22:39:57541ExtensionsServiceBackend::ExtensionsServiceBackend(
542 const FilePath& install_directory, ResourceDispatcherHost* rdh,
[email protected]27b985d2009-06-25 17:53:15543 MessageLoop* frontend_loop, bool extensions_enabled)
[email protected]0c7bc4b2009-05-30 01:47:08544 : frontend_(NULL),
545 install_directory_(install_directory),
[email protected]894bb502009-05-21 22:39:57546 resource_dispatcher_host_(rdh),
[email protected]0c7bc4b2009-05-30 01:47:08547 alert_on_error_(false),
[email protected]abe7a8942009-06-23 05:14:29548 frontend_loop_(frontend_loop),
[email protected]36a784c2009-06-23 06:21:08549 extensions_enabled_(extensions_enabled) {
[email protected]a1257b12009-06-12 02:51:34550 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01551 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15552 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34553#if defined(OS_WIN)
554 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01555 linked_ptr<ExternalExtensionProvider>(
556 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34557#endif
558}
559
560ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57561}
562
[email protected]e72e8eb82009-06-18 17:21:51563void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19564 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51565 InstalledExtensions* installed) {
566 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15567 frontend_ = frontend;
568 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15569
[email protected]e72e8eb82009-06-18 17:21:51570 // Call LoadInstalledExtension for each extension |installed| knows about.
571 scoped_ptr<InstalledExtensions::Callback> callback(
572 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
573 installed->VisitInstalledExtensions(callback.get());
574
575 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
576 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32577}
[email protected]b0beaa662009-02-26 00:04:15578
[email protected]9f1087e2009-06-15 17:29:32579void ExtensionsServiceBackend::GarbageCollectExtensions(
580 scoped_refptr<ExtensionsService> frontend) {
581 frontend_ = frontend;
582 alert_on_error_ = false;
583
584 // Nothing to clean up if it doesn't exist.
585 if (!file_util::DirectoryExists(install_directory_))
586 return;
[email protected]93fd78f42009-07-10 16:43:17587
[email protected]0b9fca72009-07-09 00:03:46588 FilePath install_directory_absolute(install_directory_);
589 file_util::AbsolutePath(&install_directory_absolute);
[email protected]540f91b2009-03-26 19:37:43590
[email protected]bf24d2c2009-02-24 23:07:45591 LOG(INFO) << "Loading installed extensions...";
592
[email protected]6014d672008-12-05 00:38:25593 // Find all child directories in the install directory and load their
594 // manifests. Post errors and results to the frontend.
[email protected]0b9fca72009-07-09 00:03:46595 file_util::FileEnumerator enumerator(install_directory_absolute,
[email protected]a1257b12009-06-12 02:51:34596 false, // Not recursive.
[email protected]6014d672008-12-05 00:38:25597 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51598 FilePath extension_path;
599 for (extension_path = enumerator.Next(); !extension_path.value().empty();
600 extension_path = enumerator.Next()) {
601 std::string extension_id = WideToASCII(
602 extension_path.BaseName().ToWStringHack());
[email protected]5a2721f62009-06-13 07:08:20603
[email protected]96088fb2009-05-26 19:08:13604 // The utility process might be in the middle of unpacking an extension, so
605 // ignore the temp unpacking directory.
606 if (extension_id == kUnpackExtensionDir)
607 continue;
[email protected]894bb502009-05-21 22:39:57608
609 // If there is no Current Version file, just delete the directory and move
610 // on. This can legitimately happen when an uninstall does not complete, for
611 // example, when a plugin is in use at uninstall time.
612 FilePath current_version_path = extension_path.AppendASCII(
613 ExtensionsService::kCurrentVersionFileName);
614 if (!file_util::PathExists(current_version_path)) {
615 LOG(INFO) << "Deleting incomplete install for directory "
616 << WideToASCII(extension_path.ToWStringHack()) << ".";
[email protected]25b34332009-06-05 21:53:19617 file_util::Delete(extension_path, true); // Recursive.
[email protected]894bb502009-05-21 22:39:57618 continue;
619 }
620
[email protected]9f1087e2009-06-15 17:29:32621 // Ignore directories that aren't valid IDs.
622 if (!Extension::IdIsValid(extension_id)) {
623 LOG(WARNING) << "Invalid extension ID encountered in extensions "
624 "directory: " << extension_id;
625 // TODO(erikkay) delete these eventually too...
[email protected]cc5da332009-03-04 08:02:51626 continue;
627 }
628
[email protected]9f1087e2009-06-15 17:29:32629 // TODO(erikkay) check for extensions that aren't loaded?
[email protected]6014d672008-12-05 00:38:25630 }
[email protected]6014d672008-12-05 00:38:25631}
632
[email protected]b0beaa662009-02-26 00:04:15633void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57634 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15635 frontend_ = frontend;
636
637 // Explicit UI loads are always noisy.
638 alert_on_error_ = true;
639
[email protected]cc5da332009-03-04 08:02:51640 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50641 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45642
643 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51644 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45645
[email protected]5bfb1eb0a2009-04-08 18:33:30646 Extension* extension = LoadExtension(extension_path,
[email protected]a1257b12009-06-12 02:51:34647 Extension::LOAD,
[email protected]e72e8eb82009-06-18 17:21:51648 false); // Don't require id.
[email protected]0877fd92009-02-03 16:34:06649 if (extension) {
650 ExtensionList* extensions = new ExtensionList;
651 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15652 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06653 }
[email protected]0877fd92009-02-03 16:34:06654}
655
[email protected]e72e8eb82009-06-18 17:21:51656void ExtensionsServiceBackend::LoadInstalledExtension(
657 const std::string& id, const FilePath& path, Extension::Location location) {
658 if (CheckExternalUninstall(id, location)) {
[email protected]27b985d2009-06-25 17:53:15659 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
660 frontend_,
661 &ExtensionsService::UninstallExtension,
662 id, true));
[email protected]e72e8eb82009-06-18 17:21:51663
664 // No error needs to be reported. The extension effectively doesn't exist.
665 return;
666 }
667
668 Extension* extension =
669 LoadExtension(FilePath(path), location, true); // Require id.
670
671 // TODO(erikkay) now we only report a single extension loaded at a time.
672 // Perhaps we should change the notifications to remove ExtensionList.
673 ExtensionList* extensions = new ExtensionList;
674 if (extension)
675 extensions->push_back(extension);
676 ReportExtensionsLoaded(extensions);
677}
678
[email protected]fbcc40302009-06-12 20:45:45679DictionaryValue* ExtensionsServiceBackend::ReadManifest(FilePath manifest_path,
680 std::string* error) {
681 JSONFileValueSerializer serializer(manifest_path);
682 scoped_ptr<Value> root(serializer.Deserialize(error));
683 if (!root.get())
684 return NULL;
685
686 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cb691e82009-07-13 14:59:01687 *error = extension_manifest_errors::kInvalidManifest;
[email protected]fbcc40302009-06-12 20:45:45688 return NULL;
689 }
690
691 return static_cast<DictionaryValue*>(root.release());
692}
693
[email protected]cc5da332009-03-04 08:02:51694Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]a1257b12009-06-12 02:51:34695 const FilePath& extension_path,
696 Extension::Location location,
697 bool require_id) {
[email protected]0877fd92009-02-03 16:34:06698 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51699 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06700 if (!file_util::PathExists(manifest_path)) {
[email protected]cb691e82009-07-13 14:59:01701 ReportExtensionLoadError(extension_path,
702 extension_manifest_errors::kInvalidManifest);
[email protected]0877fd92009-02-03 16:34:06703 return NULL;
704 }
705
[email protected]0877fd92009-02-03 16:34:06706 std::string error;
[email protected]fbcc40302009-06-12 20:45:45707 scoped_ptr<DictionaryValue> root(ReadManifest(manifest_path, &error));
[email protected]0877fd92009-02-03 16:34:06708 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51709 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06710 return NULL;
711 }
712
[email protected]cc5da332009-03-04 08:02:51713 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]fbcc40302009-06-12 20:45:45714 if (!extension->InitFromValue(*root.get(), require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51715 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06716 return NULL;
717 }
[email protected]8d6d9ff2009-02-20 08:14:39718
[email protected]a1257b12009-06-12 02:51:34719 extension->set_location(location);
[email protected]631cf822009-05-15 07:01:25720
[email protected]12198912009-06-05 03:41:22721 // Theme resource validation.
722 if (extension->IsTheme()) {
723 DictionaryValue* images_value = extension->GetThemeImages();
724 DictionaryValue::key_iterator iter = images_value->begin_keys();
725 while (iter != images_value->end_keys()) {
726 std::string val;
[email protected]25b34332009-06-05 21:53:19727 if (images_value->GetString(*iter , &val)) {
[email protected]12198912009-06-05 03:41:22728 FilePath image_path = extension->path().AppendASCII(val);
729 if (!file_util::PathExists(image_path)) {
730 ReportExtensionLoadError(extension_path,
731 StringPrintf("Could not load '%s' for theme.",
732 WideToUTF8(image_path.ToWStringHack()).c_str()));
733 return NULL;
734 }
735 }
736 ++iter;
737 }
738
739 // Themes cannot contain other extension types.
740 return extension.release();
741 }
742
[email protected]d24070e22009-05-21 19:26:59743 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24744 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
745 const UserScript& script = extension->content_scripts()[i];
746
747 for (size_t j = 0; j < script.js_scripts().size(); j++) {
748 const FilePath& path = script.js_scripts()[j].path();
749 if (!file_util::PathExists(path)) {
750 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59751 StringPrintf("Could not load '%s' for content script.",
752 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24753 return NULL;
754 }
755 }
756
757 for (size_t j = 0; j < script.css_scripts().size(); j++) {
758 const FilePath& path = script.css_scripts()[j].path();
759 if (!file_util::PathExists(path)) {
760 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59761 StringPrintf("Could not load '%s' for content script.",
762 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24763 return NULL;
764 }
[email protected]8d6d9ff2009-02-20 08:14:39765 }
766 }
767
[email protected]c533bb22009-06-03 19:06:11768 for (size_t i = 0; i < extension->plugins().size(); ++i) {
769 const Extension::PluginInfo& plugin = extension->plugins()[i];
770 if (!file_util::PathExists(plugin.path)) {
771 ReportExtensionLoadError(extension_path,
772 StringPrintf("Could not load '%s' for plugin.",
773 WideToUTF8(plugin.path.ToWStringHack()).c_str()));
774 return NULL;
775 }
776 }
777
[email protected]d24070e22009-05-21 19:26:59778 // Validate icon location for page actions.
779 const PageActionMap& page_actions = extension->page_actions();
780 for (PageActionMap::const_iterator i(page_actions.begin());
781 i != page_actions.end(); ++i) {
782 PageAction* page_action = i->second;
[email protected]d7eaf572009-07-01 21:57:00783 const std::vector<FilePath>& icon_paths = page_action->icon_paths();
784 for (std::vector<FilePath>::const_iterator iter = icon_paths.begin();
785 iter != icon_paths.end(); ++iter) {
786 FilePath path = *iter;
787 if (!file_util::PathExists(path)) {
788 ReportExtensionLoadError(extension_path,
789 StringPrintf("Could not load icon '%s' for page action.",
790 WideToUTF8(path.ToWStringHack()).c_str()));
791 return NULL;
792 }
[email protected]d24070e22009-05-21 19:26:59793 }
794 }
795
[email protected]0877fd92009-02-03 16:34:06796 return extension.release();
797}
798
[email protected]6014d672008-12-05 00:38:25799void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51800 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15801 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51802 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00803 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19804 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18805 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25806}
807
808void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15809 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57810 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
811 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25812}
[email protected]cc655912009-01-29 23:19:19813
[email protected]b0beaa662009-02-26 00:04:15814bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
815 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28816 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15817 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28818 if (file_util::PathExists(current_version)) {
819 if (file_util::ReadFileToString(current_version, version_string)) {
820 TrimWhitespace(*version_string, TRIM_ALL, version_string);
821 return true;
822 }
823 }
824 return false;
825}
826
[email protected]fbcc40302009-06-12 20:45:45827Extension::InstallType ExtensionsServiceBackend::CompareToInstalledVersion(
828 const std::string& id,
[email protected]b0beaa662009-02-26 00:04:15829 const std::string& new_version_str,
[email protected]fbcc40302009-06-12 20:45:45830 std::string *current_version_str) {
831 CHECK(current_version_str);
832 FilePath dir(install_directory_.AppendASCII(id.c_str()));
833 if (!ReadCurrentVersion(dir, current_version_str))
834 return Extension::NEW_INSTALL;
835
[email protected]b0beaa662009-02-26 00:04:15836 scoped_ptr<Version> current_version(
[email protected]fbcc40302009-06-12 20:45:45837 Version::GetVersionFromString(*current_version_str));
[email protected]b0beaa662009-02-26 00:04:15838 scoped_ptr<Version> new_version(
[email protected]fbcc40302009-06-12 20:45:45839 Version::GetVersionFromString(new_version_str));
840 int comp = new_version->CompareTo(*current_version);
841 if (comp > 0)
842 return Extension::UPGRADE;
843 else if (comp == 0)
844 return Extension::REINSTALL;
845 else
846 return Extension::DOWNGRADE;
847}
848
849bool ExtensionsServiceBackend::NeedsReinstall(const std::string& id,
850 const std::string& current_version) {
851 // Verify that the directory actually exists.
852 // TODO(erikkay): A further step would be to verify that the extension
853 // has actually loaded successfully.
854 FilePath dir(install_directory_.AppendASCII(id.c_str()));
855 FilePath version_dir(dir.AppendASCII(current_version));
856 return !file_util::PathExists(version_dir);
[email protected]cc655912009-01-29 23:19:19857}
858
[email protected]f0a51fb52009-03-05 12:46:38859bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15860 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19861 if (file_util::PathExists(dest_dir)) {
862 // By the time we get here, it should be safe to assume that this directory
863 // is not currently in use (it's not the current active version).
864 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51865 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19866 "Can't delete existing version directory.");
867 return false;
868 }
869 } else {
870 FilePath parent = dest_dir.DirName();
871 if (!file_util::DirectoryExists(parent)) {
872 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51873 ReportExtensionInstallError(source_dir,
874 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19875 return false;
876 }
877 }
878 }
879 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51880 ReportExtensionInstallError(source_dir,
881 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19882 return false;
883 }
884
885 return true;
886}
887
[email protected]b0beaa662009-02-26 00:04:15888bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
889 std::string version) {
[email protected]cc655912009-01-29 23:19:19890 // Write out the new CurrentVersion file.
891 // <profile>/Extension/<name>/CurrentVersion
892 FilePath current_version =
893 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
894 FilePath current_version_old =
895 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
896 if (file_util::PathExists(current_version_old)) {
897 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51898 ReportExtensionInstallError(dest_dir,
899 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19900 return false;
901 }
902 }
903 if (file_util::PathExists(current_version)) {
904 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51905 ReportExtensionInstallError(dest_dir,
906 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19907 return false;
908 }
909 }
910 net::FileStream stream;
911 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
912 if (stream.Open(current_version, flags) != 0)
913 return false;
914 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
915 // Restore the old CurrentVersion.
916 if (file_util::PathExists(current_version_old)) {
917 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38918 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19919 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15920
[email protected]cc655912009-01-29 23:19:19921 // TODO(erikkay): This is an ugly state to be in. Try harder?
922 }
923 }
[email protected]f0a51fb52009-03-05 12:46:38924 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51925 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19926 return false;
927 }
928 return true;
929}
930
[email protected]b0beaa662009-02-26 00:04:15931void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57932 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19933 LOG(INFO) << "Installing extension " << extension_path.value();
934
[email protected]b0beaa662009-02-26 00:04:15935 frontend_ = frontend;
[email protected]0e34d7892009-06-05 19:17:40936 alert_on_error_ = true;
[email protected]b0beaa662009-02-26 00:04:15937
[email protected]93fd78f42009-07-10 16:43:17938 InstallOrUpdateExtension(extension_path, std::string(), false);
[email protected]b0beaa662009-02-26 00:04:15939}
940
[email protected]e957fe52009-06-23 16:51:05941void ExtensionsServiceBackend::UpdateExtension(const std::string& id,
942 const FilePath& extension_path, bool alert_on_error,
943 scoped_refptr<ExtensionsService> frontend) {
944 LOG(INFO) << "Updating extension " << id << " " << extension_path.value();
945
946 frontend_ = frontend;
947 alert_on_error_ = alert_on_error;
948
[email protected]93fd78f42009-07-10 16:43:17949 InstallOrUpdateExtension(extension_path, id, true);
[email protected]e957fe52009-06-23 16:51:05950}
951
[email protected]1fca1492009-05-15 22:23:43952void ExtensionsServiceBackend::InstallOrUpdateExtension(
[email protected]93fd78f42009-07-10 16:43:17953 const FilePath& extension_path, const std::string& expected_id,
954 bool silent) {
[email protected]fbcc40302009-06-12 20:45:45955 std::string actual_public_key;
956 if (!ValidateSignature(extension_path, &actual_public_key))
957 return; // Failures reported within ValidateSignature().
958
959 UnpackerClient* client = new UnpackerClient(
[email protected]93fd78f42009-07-10 16:43:17960 this, extension_path, actual_public_key, expected_id, silent);
[email protected]1fca1492009-05-15 22:23:43961 client->Start();
962}
[email protected]cc655912009-01-29 23:19:19963
[email protected]fbcc40302009-06-12 20:45:45964bool ExtensionsServiceBackend::ValidateSignature(const FilePath& extension_path,
965 std::string* key_out) {
966 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
967 if (!file.get()) {
968 ReportExtensionInstallError(extension_path, "Could not open file.");
969 return NULL;
970 }
971
972 // Read and verify the header.
973 ExtensionsService::ExtensionHeader header;
974 size_t len;
975
976 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
977 // appears that we don't have any endian/alignment aware serialization
978 // code in the code base. So for now, this assumes that we're running
979 // on a little endian machine with 4 byte alignment.
980 len = fread(&header, 1, sizeof(ExtensionsService::ExtensionHeader),
981 file.get());
982 if (len < sizeof(ExtensionsService::ExtensionHeader)) {
983 ReportExtensionInstallError(extension_path, kInvalidExtensionHeaderError);
984 return false;
985 }
986 if (strncmp(ExtensionsService::kExtensionHeaderMagic, header.magic,
987 sizeof(header.magic))) {
988 ReportExtensionInstallError(extension_path, kBadMagicNumberError);
989 return false;
990 }
991 if (header.version != ExtensionsService::kCurrentVersion) {
992 ReportExtensionInstallError(extension_path, kBadVersionNumberError);
993 return false;
994 }
995 if (header.key_size > ExtensionsService::kMaxPublicKeySize ||
996 header.signature_size > ExtensionsService::kMaxSignatureSize) {
997 ReportExtensionInstallError(extension_path, kBadHeaderSizeError);
998 return false;
999 }
1000
1001 std::vector<uint8> key;
1002 key.resize(header.key_size);
1003 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
1004 if (len < header.key_size) {
1005 ReportExtensionInstallError(extension_path, kInvalidPublicKeyError);
1006 return false;
1007 }
1008
1009 std::vector<uint8> signature;
1010 signature.resize(header.signature_size);
1011 len = fread(&signature.front(), sizeof(uint8), header.signature_size,
1012 file.get());
1013 if (len < header.signature_size) {
1014 ReportExtensionInstallError(extension_path, kInvalidSignatureError);
1015 return false;
1016 }
1017
1018 // Note: this structure is an ASN.1 which encodes the algorithm used
1019 // with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447).
1020 // It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL }
1021 // TODO(aa): This needs to be factored away someplace common.
1022 const uint8 signature_algorithm[15] = {
1023 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
1024 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
1025 };
1026
1027 base::SignatureVerifier verifier;
1028 if (!verifier.VerifyInit(signature_algorithm,
1029 sizeof(signature_algorithm),
1030 &signature.front(),
1031 signature.size(),
1032 &key.front(),
1033 key.size())) {
1034 ReportExtensionInstallError(extension_path,
1035 kSignatureVerificationInitFailed);
1036 return false;
1037 }
1038
1039 unsigned char buf[1 << 12];
1040 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
1041 verifier.VerifyUpdate(buf, len);
1042
1043 if (!verifier.VerifyFinal()) {
1044 ReportExtensionInstallError(extension_path, kSignatureVerificationFailed);
1045 return false;
1046 }
1047
1048 net::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()),
1049 key.size()), key_out);
1050 return true;
1051}
1052
[email protected]1fca1492009-05-15 22:23:431053void ExtensionsServiceBackend::OnExtensionUnpacked(
1054 const FilePath& extension_path,
1055 const FilePath& temp_extension_dir,
1056 const std::string expected_id,
[email protected]902f7cd2009-05-22 19:02:191057 const DictionaryValue& manifest,
[email protected]93fd78f42009-07-10 16:43:171058 const std::vector< Tuple2<SkBitmap, FilePath> >& images,
1059 bool silent) {
[email protected]1fca1492009-05-15 22:23:431060 Extension extension;
1061 std::string error;
[email protected]902f7cd2009-05-22 19:02:191062 if (!extension.InitFromValue(manifest,
[email protected]1fca1492009-05-15 22:23:431063 true, // require ID
1064 &error)) {
1065 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
1066 return;
[email protected]0b344962009-03-31 04:21:451067 }
1068
[email protected]abe7a8942009-06-23 05:14:291069 Extension::Location location = Extension::INTERNAL;
1070 LookupExternalExtension(extension.id(), NULL, &location);
1071
1072 // We currently only allow themes and registry-installed extensions to be
1073 // installed.
1074 if (!extensions_enabled_ &&
1075 !extension.IsTheme() &&
1076 location != Extension::EXTERNAL_REGISTRY) {
[email protected]e2eb43112009-05-29 21:19:541077 ReportExtensionInstallError(extension_path,
[email protected]36a784c2009-06-23 06:21:081078 "Extensions are not enabled. Add --enable-extensions to the "
1079 "command-line to enable extensions.\n\n"
1080 "This is a temporary message and it will be removed when extensions "
1081 "UI is finalized.");
[email protected]e2eb43112009-05-29 21:19:541082 return;
1083 }
1084
[email protected]638d3522009-06-25 14:52:011085 // TODO(extensions): Make better extensions UI. http://crbug.com/12116
1086
[email protected]93fd78f42009-07-10 16:43:171087 // We don't show the install dialog for themes, updates, or external
1088 // extensions.
1089 if (!extension.IsTheme() && !silent && frontend_->show_extensions_prompts()) {
[email protected]638d3522009-06-25 14:52:011090#if defined(OS_WIN)
1091 if (!Extension::IsExternalLocation(location) &&
1092 win_util::MessageBox(GetForegroundWindow(),
1093 L"Are you sure you want to install this extension?\n\n"
1094 L"This is a temporary message and it will be removed when "
1095 L"extensions UI is finalized.",
1096 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
1097 MB_OKCANCEL) != IDOK) {
1098 ReportExtensionInstallError(extension_path,
1099 "User did not allow extension to be installed.");
1100 return;
1101 }
1102#elif defined(OS_MACOSX)
1103 // Using CoreFoundation to do this dialog is unimaginably lame but will do
1104 // until the UI is redone.
1105 scoped_cftyperef<CFStringRef> product_name(
1106 base::SysWideToCFStringRef(l10n_util::GetString(IDS_PRODUCT_NAME)));
1107 CFOptionFlags response;
1108 CFUserNotificationDisplayAlert(
1109 0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL,
1110 product_name,
1111 CFSTR("Are you sure you want to install this extension?\n\n"
1112 "This is a temporary message and it will be removed when "
1113 "extensions UI is finalized."),
1114 NULL, CFSTR("Cancel"), NULL, &response);
1115
1116 if (response == kCFUserNotificationAlternateResponse) {
1117 ReportExtensionInstallError(extension_path,
1118 "User did not allow extension to be installed.");
1119 return;
1120 }
1121#endif // OS_*
[email protected]0e34d7892009-06-05 19:17:401122 }
[email protected]0e34d7892009-06-05 19:17:401123
[email protected]b0beaa662009-02-26 00:04:151124 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:431125 if (!expected_id.empty() && expected_id != extension.id()) {
[email protected]25b34332009-06-05 21:53:191126 std::string error_msg = "ID in new extension manifest (";
1127 error_msg += extension.id();
1128 error_msg += ") does not match expected ID (";
1129 error_msg += expected_id;
1130 error_msg += ")";
1131 ReportExtensionInstallError(extension_path, error_msg);
[email protected]1fca1492009-05-15 22:23:431132 return;
[email protected]cc655912009-01-29 23:19:191133 }
1134
1135 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:151136 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:191137 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:151138 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451139 Extension::InstallType install_type =
1140 CompareToInstalledVersion(extension.id(), version, &current_version);
1141
1142 // Do not allow downgrade.
1143 if (install_type == Extension::DOWNGRADE) {
1144 ReportExtensionInstallError(extension_path,
1145 "Error: Attempt to downgrade extension from more recent version.");
1146 return;
1147 }
1148
1149 if (install_type == Extension::REINSTALL) {
1150 if (NeedsReinstall(extension.id(), current_version)) {
1151 // Treat corrupted existing installation as new install case.
1152 install_type = Extension::NEW_INSTALL;
1153 } else {
1154 // The client may use this as a signal (to switch themes, for instance).
[email protected]e957fe52009-06-23 16:51:051155 ReportExtensionOverinstallAttempted(extension.id(), extension_path);
[email protected]1fca1492009-05-15 22:23:431156 return;
[email protected]fbcc40302009-06-12 20:45:451157 }
[email protected]b0beaa662009-02-26 00:04:151158 }
[email protected]cc655912009-01-29 23:19:191159
[email protected]902f7cd2009-05-22 19:02:191160 // Write our parsed manifest back to disk, to ensure it doesn't contain an
1161 // exploitable bug that can be used to compromise the browser.
1162 std::string manifest_json;
1163 JSONStringValueSerializer serializer(&manifest_json);
1164 serializer.set_pretty_print(true);
1165 if (!serializer.Serialize(manifest)) {
1166 ReportExtensionInstallError(extension_path,
1167 "Error serializing manifest.json.");
1168 return;
1169 }
1170
1171 FilePath manifest_path =
1172 temp_extension_dir.AppendASCII(Extension::kManifestFilename);
1173 if (!file_util::WriteFile(manifest_path,
1174 manifest_json.data(), manifest_json.size())) {
1175 ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
1176 return;
1177 }
1178
[email protected]facd7a7652009-06-05 23:15:021179 // Delete any images that may be used by the browser. We're going to write
1180 // out our own versions of the parsed images, and we want to make sure the
1181 // originals are gone for good.
1182 std::set<FilePath> image_paths = extension.GetBrowserImages();
1183 if (image_paths.size() != images.size()) {
1184 ReportExtensionInstallError(extension_path,
1185 "Decoded images don't match what's in the manifest.");
1186 return;
1187 }
1188
1189 for (std::set<FilePath>::iterator it = image_paths.begin();
1190 it != image_paths.end(); ++it) {
1191 if (!file_util::Delete(temp_extension_dir.Append(*it), false)) {
1192 ReportExtensionInstallError(extension_path,
1193 "Error removing old image file.");
1194 return;
1195 }
1196 }
1197
[email protected]902f7cd2009-05-22 19:02:191198 // Write our parsed images back to disk as well.
1199 for (size_t i = 0; i < images.size(); ++i) {
1200 const SkBitmap& image = images[i].a;
1201 FilePath path = temp_extension_dir.Append(images[i].b);
1202
1203 std::vector<unsigned char> image_data;
1204 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
1205 // though they may originally be .jpg, etc. Figure something out.
1206 // http://code.google.com/p/chromium/issues/detail?id=12459
1207 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
1208 ReportExtensionInstallError(extension_path,
1209 "Error re-encoding theme image.");
1210 return;
1211 }
1212
1213 // Note: we're overwriting existing files that the utility process wrote,
1214 // so we can be sure the directory exists.
1215 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
1216 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
1217 ReportExtensionInstallError(extension_path, "Error saving theme image.");
1218 return;
1219 }
1220 }
1221
[email protected]cc655912009-01-29 23:19:191222 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:431223 FilePath version_dir = dest_dir.AppendASCII(version);
[email protected]902f7cd2009-05-22 19:02:191224
1225 // If anything fails after this, we want to delete the extension dir.
1226 ScopedTempDir scoped_version_dir;
1227 scoped_version_dir.Set(version_dir);
1228
[email protected]1fca1492009-05-15 22:23:431229 if (!InstallDirSafely(temp_extension_dir, version_dir))
1230 return;
[email protected]cc655912009-01-29 23:19:191231
[email protected]902f7cd2009-05-22 19:02:191232 if (!SetCurrentVersion(dest_dir, version))
[email protected]1fca1492009-05-15 22:23:431233 return;
[email protected]cc655912009-01-29 23:19:191234
[email protected]9f1087e2009-06-15 17:29:321235 Extension* loaded = LoadExtension(version_dir,
1236 location,
1237 true); // require id
1238 CHECK(loaded);
[email protected]1fca1492009-05-15 22:23:431239
[email protected]9f1087e2009-06-15 17:29:321240 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]e957fe52009-06-23 16:51:051241 frontend_, &ExtensionsService::OnExtensionInstalled, extension_path,
1242 loaded, install_type));
[email protected]894bb502009-05-21 22:39:571243
[email protected]9f1087e2009-06-15 17:29:321244 // Only one extension, but ReportExtensionsLoaded can handle multiple,
1245 // so we need to construct a list.
1246 scoped_ptr<ExtensionList> extensions(new ExtensionList);
1247 extensions->push_back(loaded);
[email protected]894bb502009-05-21 22:39:571248
[email protected]9f1087e2009-06-15 17:29:321249 // Hand off ownership of the loaded extensions to the frontend.
1250 ReportExtensionsLoaded(extensions.release());
[email protected]902f7cd2009-05-22 19:02:191251
1252 scoped_version_dir.Take();
[email protected]cc655912009-01-29 23:19:191253}
1254
1255void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:511256 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:151257
[email protected]cc655912009-01-29 23:19:191258 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:511259 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:191260 std::string message =
1261 StringPrintf("Could not install extension from '%s'. %s",
1262 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:181263 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]e957fe52009-06-23 16:51:051264
1265 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
1266 frontend_,
1267 &ExtensionsService::OnExtenionInstallError,
1268 extension_path));
[email protected]cc655912009-01-29 23:19:191269}
1270
[email protected]fbcc40302009-06-12 20:45:451271void ExtensionsServiceBackend::ReportExtensionOverinstallAttempted(
[email protected]e957fe52009-06-23 16:51:051272 const std::string& id, const FilePath& path) {
[email protected]894bb502009-05-21 22:39:571273 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]e957fe52009-06-23 16:51:051274 frontend_, &ExtensionsService::OnExtensionOverinstallAttempted, id,
1275 path));
[email protected]b0beaa662009-02-26 00:04:151276}
1277
[email protected]25b34332009-06-05 21:53:191278bool ExtensionsServiceBackend::ShouldSkipInstallingExtension(
1279 const std::set<std::string>& ids_to_ignore,
1280 const std::string& id) {
1281 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
1282 LOG(INFO) << "Skipping uninstalled external extension " << id;
1283 return true;
1284 }
1285 return false;
1286}
1287
1288void ExtensionsServiceBackend::CheckVersionAndInstallExtension(
[email protected]a1257b12009-06-12 02:51:341289 const std::string& id, const Version* extension_version,
[email protected]9f1087e2009-06-15 17:29:321290 const FilePath& extension_path) {
[email protected]25b34332009-06-05 21:53:191291 if (ShouldInstall(id, extension_version))
[email protected]93fd78f42009-07-10 16:43:171292 InstallOrUpdateExtension(FilePath(extension_path), id, false);
[email protected]25b34332009-06-05 21:53:191293}
1294
[email protected]a1257b12009-06-12 02:51:341295bool ExtensionsServiceBackend::LookupExternalExtension(
1296 const std::string& id, Version** version, Extension::Location* location) {
1297 scoped_ptr<Version> extension_version;
1298 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1299 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011300 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341301 extension_version.reset(provider->RegisteredVersion(id, location));
1302 if (extension_version.get()) {
1303 if (version)
1304 *version = extension_version.release();
1305 return true;
1306 }
1307 }
1308 return false;
1309}
1310
[email protected]b0beaa662009-02-26 00:04:151311// Some extensions will autoupdate themselves externally from Chrome. These
1312// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191313// these, the extension will register its location in the the preferences file
1314// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151315// check that location for a .crx file, which it will then install locally if
1316// a new version is available.
1317void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571318 std::set<std::string> ids_to_ignore,
1319 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151320 // Note that this installation is intentionally silent (since it didn't
1321 // go through the front-end). Extensions that are registered in this
1322 // way are effectively considered 'pre-bundled', and so implicitly
1323 // trusted. In general, if something has HKLM or filesystem access,
1324 // they could install an extension manually themselves anyway.
1325 alert_on_error_ = false;
1326 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151327
[email protected]a1257b12009-06-12 02:51:341328 // Ask each external extension provider to give us a call back for each
1329 // extension they know about. See OnExternalExtensionFound.
1330 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1331 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011332 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341333 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191334 }
[email protected]b0beaa662009-02-26 00:04:151335}
1336
[email protected]25b34332009-06-05 21:53:191337bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:511338 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341339 // Check if the providers know about this extension.
1340 ProviderMap::const_iterator i = external_extension_providers_.find(location);
1341 if (i != external_extension_providers_.end()) {
1342 scoped_ptr<Version> version;
1343 version.reset(i->second->RegisteredVersion(id, NULL));
1344 if (version.get())
1345 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:511346 } else {
1347 // Not from an external provider, so it's fine.
1348 return false;
[email protected]b0beaa662009-02-26 00:04:151349 }
[email protected]25b34332009-06-05 21:53:191350
[email protected]a1257b12009-06-12 02:51:341351 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:151352}
1353
1354// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:251355void ExtensionsServiceBackend::UninstallExtension(
1356 const std::string& extension_id) {
1357 // First, delete the Current Version file. If the directory delete fails, then
1358 // at least the extension won't be loaded again.
1359 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
1360
1361 if (!file_util::PathExists(extension_directory)) {
1362 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:151363 return;
1364 }
[email protected]631cf822009-05-15 07:01:251365
1366 FilePath current_version_file = extension_directory.AppendASCII(
1367 ExtensionsService::kCurrentVersionFileName);
1368 if (!file_util::PathExists(current_version_file)) {
1369 LOG(WARNING) << "Extension " << extension_id
1370 << " does not have a Current Version file.";
1371 } else {
1372 if (!file_util::Delete(current_version_file, false)) {
1373 LOG(WARNING) << "Could not delete Current Version file for extension "
1374 << extension_id;
1375 return;
1376 }
1377 }
1378
[email protected]a1257b12009-06-12 02:51:341379 // OK, now try and delete the entire rest of the directory. One major place
[email protected]894bb502009-05-21 22:39:571380 // this can fail is if the extension contains a plugin (stupid plugins). It's
1381 // not a big deal though, because we'll notice next time we startup that the
1382 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:251383 if (!file_util::Delete(extension_directory, true)) {
1384 LOG(WARNING) << "Could not delete directory for extension "
1385 << extension_id;
[email protected]b0beaa662009-02-26 00:04:151386 }
1387}
1388
[email protected]a1257b12009-06-12 02:51:341389void ExtensionsServiceBackend::ClearProvidersForTesting() {
1390 external_extension_providers_.clear();
1391}
1392
1393void ExtensionsServiceBackend::SetProviderForTesting(
1394 Extension::Location location,
1395 ExternalExtensionProvider* test_provider) {
1396 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011397 external_extension_providers_[location] =
1398 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341399}
1400
1401void ExtensionsServiceBackend::OnExternalExtensionFound(
1402 const std::string& id, const Version* version, const FilePath& path) {
[email protected]9f1087e2009-06-15 17:29:321403 CheckVersionAndInstallExtension(id, version, path);
[email protected]a1257b12009-06-12 02:51:341404}
1405
[email protected]b0beaa662009-02-26 00:04:151406bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
[email protected]a1257b12009-06-12 02:51:341407 const Version* version) {
[email protected]b0beaa662009-02-26 00:04:151408 std::string current_version;
[email protected]fbcc40302009-06-12 20:45:451409 Extension::InstallType install_type =
1410 CompareToInstalledVersion(id, version->GetString(), &current_version);
1411
1412 if (install_type == Extension::DOWNGRADE)
1413 return false;
1414
1415 return (install_type == Extension::UPGRADE ||
1416 install_type == Extension::NEW_INSTALL ||
1417 NeedsReinstall(id, current_version));
[email protected]cc655912009-01-29 23:19:191418}