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