crx_installer.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/crx_installer.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <set> 8513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/l10n_util.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/resource_bundle.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 1221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/lazy_instance.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/scoped_temp_dir.h" 154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/stl_util-inl.h" 163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stringprintf.h" 17201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/time.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h" 19513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/thread_restrictions.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/version.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h" 23731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/browser_thread.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/convert_user_script.h" 25201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/extensions/convert_web_app.h" 2621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_error_reporter.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/shell_integration.h" 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/web_applications/web_app.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h" 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_file_util.h" 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h" 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/notification_service.h" 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/notification_type.h" 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/chromium_strings.h" 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h" 373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "grit/theme_resources.h" 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h" 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Helper function to delete files. This is used to avoid ugly casts which 433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// would be necessary with PostMessage since file_util::Delete is overloaded. 443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickstatic void DeleteFileHelper(const FilePath& path, bool recursive) { 453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick file_util::Delete(path, recursive); 463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickstruct WhitelistedInstallData { 493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick WhitelistedInstallData() {} 504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch std::set<std::string> ids; 513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}; 523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 5321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstatic base::LazyInstance<WhitelistedInstallData> 5421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen g_whitelisted_install_data(base::LINKER_INITIALIZED); 5521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 56513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} // namespace 573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CrxInstaller::SetWhitelistedInstallId(const std::string& id) { 60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 6121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen g_whitelisted_install_data.Get().ids.insert(id); 623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static 654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CrxInstaller::IsIdWhitelisted(const std::string& id) { 66513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 6721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen std::set<std::string>& ids = g_whitelisted_install_data.Get().ids; 684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return ContainsKey(ids, id); 694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch} 703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// static 724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CrxInstaller::ClearWhitelistedInstallId(const std::string& id) { 734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 7421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen std::set<std::string>& ids = g_whitelisted_install_data.Get().ids; 754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if (ContainsKey(ids, id)) { 764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch ids.erase(id); 773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return true; 783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 8221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenCrxInstaller::CrxInstaller(ExtensionService* frontend, 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionInstallUI* client) 84201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch : install_directory_(frontend->install_directory()), 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch install_source_(Extension::INTERNAL), 86201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch extensions_enabled_(frontend->extensions_enabled()), 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete_source_(false), 88731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick is_gallery_install_(false), 89731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick create_app_shortcut_(false), 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch frontend_(frontend), 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_(client), 923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick apps_require_extension_mime_type_(false), 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick allow_silent_install_(false) { 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCrxInstaller::~CrxInstaller() { 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Delete the temp directory and crx file as necessary. Note that the 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // destructor might be called on any thread, so we post a task to the file 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // thread to make sure the delete happens there. 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!temp_dir_.value().empty()) { 101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 102731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableFunction(&DeleteFileHelper, temp_dir_, true)); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (delete_source_) { 107731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 108731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableFunction(&DeleteFileHelper, source_file_, false)); 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Make sure the UI is deleted on the ui thread. 113731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_); 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_ = NULL; 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallCrx(const FilePath& source_file) { 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source_file_ = source_file; 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath user_data_temp_dir; 121513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch { 122513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // We shouldn't be doing disk IO on the UI thread. 123513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // http://code.google.com/p/chromium/issues/detail?id=60634 124513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch base::ThreadRestrictions::ScopedAllowIO allow_io; 125513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); 126513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_refptr<SandboxedExtensionUnpacker> unpacker( 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new SandboxedExtensionUnpacker( 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source_file, 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch user_data_temp_dir, 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->resource_dispatcher_host(), 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this)); 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 135731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod( 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unpacker.get(), &SandboxedExtensionUnpacker::Start)); 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallUserScript(const FilePath& source_file, 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& original_url) { 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!original_url.is_empty()); 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch source_file_ = source_file; 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch original_url_ = original_url; 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 149731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::ConvertUserScriptOnFileThread)); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ConvertUserScriptOnFileThread() { 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string error; 155513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch scoped_refptr<Extension> extension = 156513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch ConvertUserScriptToExtension(source_file_, original_url_, &error); 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!extension) { 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromFileThread(error); 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch OnUnpackSuccess(extension->path(), extension->path(), extension); 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 165201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) { 166201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::PostTask( 167201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch BrowserThread::FILE, FROM_HERE, 168201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NewRunnableMethod(this, &CrxInstaller::ConvertWebAppOnFileThread, 169201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch web_app)); 170201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch} 171201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 172201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid CrxInstaller::ConvertWebAppOnFileThread( 173201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch const WebApplicationInfo& web_app) { 174201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch std::string error; 175201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch scoped_refptr<Extension> extension( 176201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch ConvertWebAppToExtension(web_app, base::Time::Now())); 177201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!extension) { 178201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Validation should have stopped any potential errors before getting here. 179201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NOTREACHED() << "Could not convert web app to extension."; 180201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return; 181201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch } 182201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 183201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // TODO(aa): conversion data gets lost here :( 184201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 185201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch OnUnpackSuccess(extension->path(), extension->path(), extension); 186201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch} 187201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 188513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool CrxInstaller::AllowInstall(const Extension* extension, 189513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch std::string* error) { 190731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(error); 191731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 192731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // We always allow themes and external installs. 193731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (extension->is_theme() || Extension::IsExternalLocation(install_source_)) 194731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return true; 195731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 196731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!extensions_enabled_) { 197731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *error = "Extensions are not enabled."; 198731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 199731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 200731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 201731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Make sure the expected id matches. 202731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // TODO(aa): Also support expected version? 203731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!expected_id_.empty() && expected_id_ != extension->id()) { 204731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *error = base::StringPrintf( 205731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "ID in new extension manifest (%s) does not match expected id (%s)", 206731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick extension->id().c_str(), 207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick expected_id_.c_str()); 208731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 209731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (extension_->is_app()) { 212731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // If the app was downloaded, apps_require_extension_mime_type_ 213731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // will be set. In this case, check that it was served with the 214731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // right mime type. Make an exception for file URLs, which come 215731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // from the users computer and have no headers. 216731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!original_url_.SchemeIsFile() && 217731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick apps_require_extension_mime_type_ && 218731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick original_mime_type_ != Extension::kMimeType) { 219731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *error = base::StringPrintf( 220731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "Apps must be served with content type %s.", 221731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick Extension::kMimeType); 222731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 223731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 224731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 225731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // If the client_ is NULL, then the app is either being installed via 226731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // an internal mechanism like sync, external_extensions, or default apps. 227731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // In that case, we don't want to enforce things like the install origin. 228731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!is_gallery_install_ && client_) { 229731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // For apps with a gallery update URL, require that they be installed 230731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // from the gallery. 231731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // TODO(erikkay) Apply this rule for paid extensions and themes as well. 2324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if (extension->UpdatesFromGallery()) { 233731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *error = l10n_util::GetStringFUTF8( 234731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS, 235731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)); 236731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 237731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 238731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 239731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // For self-hosted apps, verify that the entire extent is on the same 240731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // host (or a subdomain of the host) the download happened from. There's 241731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // no way for us to verify that the app controls any other hosts. 242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick URLPattern pattern(UserScript::kValidUserScriptSchemes); 243731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick pattern.set_host(original_url_.host()); 244731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick pattern.set_match_subdomains(true); 245731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 246731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ExtensionExtent::PatternList patterns = 247731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick extension_->web_extent().patterns(); 248731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick for (size_t i = 0; i < patterns.size(); ++i) { 249731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!pattern.MatchesHost(patterns[i].host())) { 250731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *error = base::StringPrintf( 251731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "Apps must be served from the host that they affect."); 252731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 253731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 254731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 255731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 256731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 257731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 258731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return true; 259731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick} 260731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::OnUnpackFailure(const std::string& error_message) { 262731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromFileThread(error_message); 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& extension_dir, 268513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const Extension* extension) { 269731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Note: We take ownership of |extension| and |temp_dir|. 272513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch extension_ = extension; 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch temp_dir_ = temp_dir; 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We don't have to delete the unpack dir explicity since it is a child of 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the temp dir. 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unpacked_extension_root_ = extension_dir; 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 279731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string error; 280731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!AllowInstall(extension, &error)) { 281731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ReportFailureFromFileThread(error); 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 285ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch if (client_) { 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Extension::DecodeIcon(extension_.get(), Extension::EXTENSION_ICON_LARGE, 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &install_icon_); 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 290731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 291731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::UI, FROM_HERE, 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::ConfirmInstall)); 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ConfirmInstall() { 296731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) { 298513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch VLOG(1) << "This extension: " << extension_->id() 299513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch << " is blacklisted. Install failed."; 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromUIThread("This extension is blacklisted."); 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!frontend_->extension_prefs()->IsExtensionAllowedByPolicy( 3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick extension_->id())) { 3063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ReportFailureFromUIThread("This extension is blacklisted by admin policy."); 3073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 3083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 3093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL overlapping_url; 311513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const Extension* overlapping_extension = 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch frontend_->GetExtensionByOverlappingWebExtent(extension_->web_extent()); 31321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (overlapping_extension) { 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromUIThread(l10n_util::GetStringFUTF8( 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IDS_EXTENSION_OVERLAPPING_WEB_EXTENT, 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UTF8ToUTF16(overlapping_extension->name()))); 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch current_version_ = 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch frontend_->extension_prefs()->GetVersionString(extension_->id()); 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 323513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch bool whitelisted = ClearWhitelistedInstallId(extension_->id()) && 3244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch extension_->plugins().empty() && is_gallery_install_; 325513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 3263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (client_ && 327513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch (!allow_silent_install_ || !whitelisted)) { 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AddRef(); // Balanced in Proceed() and Abort(). 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_->ConfirmInstall(this, extension_.get()); 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 331731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 332731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::CompleteInstall)); 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CrxInstaller::InstallUIProceed() { 339731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 340731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::CompleteInstall)); 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Release(); // balanced in ConfirmInstall(). 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallUIAbort() { 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Kill the theme loading bubble. 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService* service = NotificationService::current(); 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch service->Notify(NotificationType::NO_THEME_DETECTED, 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<CrxInstaller>(this), 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::NoDetails()); 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Release(); // balanced in ConfirmInstall(). 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We're done. Since we don't post any more tasks to ourself, our ref count 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // should go to zero and we die. The destructor will clean up the temp dir. 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::CompleteInstall() { 359731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!current_version_.empty()) { 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<Version> current_version( 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Version::GetVersionFromString(current_version_)); 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (current_version->CompareTo(*(extension_->version())) > 0) { 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromFileThread("Attempted to downgrade extension."); 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath version_dir = extension_file_util::InstallExtension( 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unpacked_extension_root_, 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_->id(), 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_->VersionString(), 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch install_directory_); 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (version_dir.empty()) { 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportFailureFromFileThread( 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch l10n_util::GetStringUTF8( 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)); 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This is lame, but we must reload the extension because absolute paths 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // inside the content scripts are established inside InitFromValue() and we 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // just moved the extension. 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(aa): All paths to resources inside extensions should be created 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // lazily and based on the Extension's root path at that moment. 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string error; 388513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch extension_ = extension_file_util::LoadExtension( 389513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch version_dir, install_source_, true, &error); 390201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch CHECK(error.empty()) << error; 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportSuccessFromFileThread(); 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportFailureFromFileThread(const std::string& error) { 396731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 397731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 398731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::UI, FROM_HERE, 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::ReportFailureFromUIThread, error)); 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportFailureFromUIThread(const std::string& error) { 403731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService* service = NotificationService::current(); 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch service->Notify(NotificationType::EXTENSION_INSTALL_ERROR, 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<CrxInstaller>(this), 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Details<const std::string>(&error)); 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This isn't really necessary, it is only used because unit tests expect to 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // see errors get reported via this interface. 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(aa): Need to go through unit tests and clean them up too, probably get 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // rid of this line. 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (client_) 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_->OnInstallFailure(error); 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportSuccessFromFileThread() { 422731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 423731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 424731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::UI, FROM_HERE, 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &CrxInstaller::ReportSuccessFromUIThread)); 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportSuccessFromUIThread() { 429731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there is a client, tell the client about installation. 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (client_) 433ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch client_->OnInstallSuccess(extension_.get(), install_icon_.get()); 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tell the frontend about the installation and hand off ownership of 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // extension_ to it. 437201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch frontend_->OnExtensionInstalled(extension_); 438513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch extension_ = NULL; 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We're done. We don't post any more tasks to ourselves so we are deleted 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // soon. 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 443