172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 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
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <map>
84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <set>
9513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/lazy_instance.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/metrics/histogram.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h"
154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/stl_util-inl.h"
163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stringprintf.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h"
183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h"
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/time.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/version.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/convert_user_script.h"
24201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "chrome/browser/extensions/convert_web_app.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_error_reporter.h"
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/extensions/extension_service.h"
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/shell_integration.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/web_applications/web_app.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h"
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/extensions/extension_file_util.h"
32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h"
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/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"
3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickstruct WhitelistedInstallData {
453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  WhitelistedInstallData() {}
464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::set<std::string> ids;
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::map<std::string, linked_ptr<DictionaryValue> > manifests;
483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
5021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstatic base::LazyInstance<WhitelistedInstallData>
5121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    g_whitelisted_install_data(base::LINKER_INITIALIZED);
5221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
53513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}  // namespace
543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static
563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CrxInstaller::SetWhitelistedInstallId(const std::string& id) {
57731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
5821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  g_whitelisted_install_data.Get().ids.insert(id);
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static
62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CrxInstaller::SetWhitelistedManifest(const std::string& id,
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                          DictionaryValue* parsed_manifest) {
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  WhitelistedInstallData& data = g_whitelisted_install_data.Get();
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  data.manifests[id] = linked_ptr<DictionaryValue>(parsed_manifest);
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// static
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst DictionaryValue* CrxInstaller::GetWhitelistedManifest(
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const std::string& id) {
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  WhitelistedInstallData& data = g_whitelisted_install_data.Get();
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (ContainsKey(data.manifests, id))
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return data.manifests[id].get();
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  else
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return NULL;
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// static
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenDictionaryValue* CrxInstaller::RemoveWhitelistedManifest(
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const std::string& id) {
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  WhitelistedInstallData& data = g_whitelisted_install_data.Get();
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (ContainsKey(data.manifests, id)) {
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DictionaryValue* manifest = data.manifests[id].release();
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    data.manifests.erase(id);
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return manifest;
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return NULL;
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// static
924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CrxInstaller::IsIdWhitelisted(const std::string& id) {
93513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
9421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  std::set<std::string>& ids = g_whitelisted_install_data.Get().ids;
954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return ContainsKey(ids, id);
964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// static
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CrxInstaller::ClearWhitelistedInstallId(const std::string& id) {
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
10121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  std::set<std::string>& ids = g_whitelisted_install_data.Get().ids;
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (ContainsKey(ids, id)) {
1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    ids.erase(id);
1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return true;
1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return false;
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenCrxInstaller::CrxInstaller(ExtensionService* frontend,
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           ExtensionInstallUI* client)
111201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    : install_directory_(frontend->install_directory()),
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      install_source_(Extension::INTERNAL),
113201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      extensions_enabled_(frontend->extensions_enabled()),
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      delete_source_(false),
115731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      is_gallery_install_(false),
116731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      create_app_shortcut_(false),
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frontend_(frontend),
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      client_(client),
1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      apps_require_extension_mime_type_(false),
1203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      allow_silent_install_(false) {
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCrxInstaller::~CrxInstaller() {
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Delete the temp directory and crx file as necessary. Note that the
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // destructor might be called on any thread, so we post a task to the file
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // thread to make sure the delete happens there.
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!temp_dir_.value().empty()) {
128731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
129731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::FILE, FROM_HERE,
130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        NewRunnableFunction(
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen            &extension_file_util::DeleteFile, temp_dir_, true));
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (delete_source_) {
135731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::FILE, FROM_HERE,
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        NewRunnableFunction(
138dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen            &extension_file_util::DeleteFile, source_file_, false));
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the UI is deleted on the ui thread.
142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  client_ = NULL;
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallCrx(const FilePath& source_file) {
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  source_file_ = source_file;
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_refptr<SandboxedExtensionUnpacker> unpacker(
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new SandboxedExtensionUnpacker(
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          source_file,
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          g_browser_process->resource_dispatcher_host(),
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          this));
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::FILE, FROM_HERE,
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          unpacker.get(), &SandboxedExtensionUnpacker::Start));
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallUserScript(const FilePath& source_file,
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     const GURL& original_url) {
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!original_url.is_empty());
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  source_file_ = source_file;
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  original_url_ = original_url;
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
169731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::FILE, FROM_HERE,
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this, &CrxInstaller::ConvertUserScriptOnFileThread));
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ConvertUserScriptOnFileThread() {
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string error;
175513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  scoped_refptr<Extension> extension =
176513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      ConvertUserScriptToExtension(source_file_, original_url_, &error);
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!extension) {
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ReportFailureFromFileThread(error);
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  OnUnpackSuccess(extension->path(), extension->path(), extension);
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
185201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
186201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  BrowserThread::PostTask(
187201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      BrowserThread::FILE, FROM_HERE,
188201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      NewRunnableMethod(this, &CrxInstaller::ConvertWebAppOnFileThread,
189201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                        web_app));
190201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch}
191201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
192201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid CrxInstaller::ConvertWebAppOnFileThread(
193201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    const WebApplicationInfo& web_app) {
194201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  std::string error;
195201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  scoped_refptr<Extension> extension(
196201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      ConvertWebAppToExtension(web_app, base::Time::Now()));
197201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  if (!extension) {
198201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // Validation should have stopped any potential errors before getting here.
199201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    NOTREACHED() << "Could not convert web app to extension.";
200201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    return;
201201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  }
202201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
203201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  // TODO(aa): conversion data gets lost here :(
204201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
205201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  OnUnpackSuccess(extension->path(), extension->path(), extension);
206201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch}
207201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch
208513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool CrxInstaller::AllowInstall(const Extension* extension,
209513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                std::string* error) {
210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(error);
211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
212731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Make sure the expected id matches.
213731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!expected_id_.empty() && expected_id_ != extension->id()) {
214731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *error = base::StringPrintf(
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        "ID in new CRX manifest (%s) does not match expected id (%s)",
216731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        extension->id().c_str(),
217731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        expected_id_.c_str());
218731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return false;
219731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
220731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (expected_version_.get() &&
222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      !expected_version_->Equals(*extension->version())) {
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    *error = base::StringPrintf(
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        "Version in new CRX %s manifest (%s) does not match expected "
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        "version (%s)",
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        extension->id().c_str(),
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        expected_version_->GetString().c_str(),
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        extension->version()->GetString().c_str());
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // The checks below are skipped for themes and external installs.
233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (extension->is_theme() || Extension::IsExternalLocation(install_source_))
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return true;
235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!extensions_enabled_) {
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    *error = "Extensions are not enabled.";
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
241731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (extension_->is_app()) {
242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // If the app was downloaded, apps_require_extension_mime_type_
243731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // will be set.  In this case, check that it was served with the
244731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // right mime type.  Make an exception for file URLs, which come
245731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // from the users computer and have no headers.
246731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (!original_url_.SchemeIsFile() &&
247731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        apps_require_extension_mime_type_ &&
248731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        original_mime_type_ != Extension::kMimeType) {
249731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *error = base::StringPrintf(
250731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          "Apps must be served with content type %s.",
251731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          Extension::kMimeType);
252731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return false;
253731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
254731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
255731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // If the client_ is NULL, then the app is either being installed via
256731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // an internal mechanism like sync, external_extensions, or default apps.
257731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // In that case, we don't want to enforce things like the install origin.
258731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (!is_gallery_install_ && client_) {
259731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // For apps with a gallery update URL, require that they be installed
260731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // from the gallery.
261731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // TODO(erikkay) Apply this rule for paid extensions and themes as well.
2624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      if (extension->UpdatesFromGallery()) {
263731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        *error = l10n_util::GetStringFUTF8(
264731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick            IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
265731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick            l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
266731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        return false;
267731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      }
268731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
269731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // For self-hosted apps, verify that the entire extent is on the same
270731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // host (or a subdomain of the host) the download happened from.  There's
271731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // no way for us to verify that the app controls any other hosts.
272731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      URLPattern pattern(UserScript::kValidUserScriptSchemes);
273731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      pattern.set_host(original_url_.host());
274731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      pattern.set_match_subdomains(true);
275731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
276731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      ExtensionExtent::PatternList patterns =
277731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          extension_->web_extent().patterns();
278731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      for (size_t i = 0; i < patterns.size(); ++i) {
279731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        if (!pattern.MatchesHost(patterns[i].host())) {
280731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          *error = base::StringPrintf(
281731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick              "Apps must be served from the host that they affect.");
282731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          return false;
283731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        }
284731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      }
285731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
286731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
287731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
288731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return true;
289731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
290731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::OnUnpackFailure(const std::string& error_message) {
292731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ReportFailureFromFileThread(error_message);
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const FilePath& extension_dir,
298513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                   const Extension* extension) {
299731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Note: We take ownership of |extension| and |temp_dir|.
302513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  extension_ = extension;
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  temp_dir_ = temp_dir;
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We don't have to delete the unpack dir explicity since it is a child of
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the temp dir.
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  unpacked_extension_root_ = extension_dir;
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
309731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string error;
310731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!AllowInstall(extension, &error)) {
311731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ReportFailureFromFileThread(error);
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
315ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch  if (client_) {
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Extension::DecodeIcon(extension_.get(), Extension::EXTENSION_ICON_LARGE,
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          &install_icon_);
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
320731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
321731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI, FROM_HERE,
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this, &CrxInstaller::ConfirmInstall));
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Helper method to let us compare a whitelisted manifest with the actual
326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// downloaded extension's manifest, but ignoring the kPublicKey since the
327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// whitelisted manifest doesn't have that value.
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenstatic bool EqualsIgnoringPublicKey(
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const DictionaryValue& extension_manifest,
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const DictionaryValue& whitelisted_manifest) {
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<DictionaryValue> manifest_copy(extension_manifest.DeepCopy());
332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  manifest_copy->Remove(extension_manifest_keys::kPublicKey, NULL);
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return manifest_copy->Equals(&whitelisted_manifest);
334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ConfirmInstall() {
337731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) {
339513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    VLOG(1) << "This extension: " << extension_->id()
340513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch            << " is blacklisted. Install failed.";
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ReportFailureFromUIThread("This extension is blacklisted.");
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!frontend_->extension_prefs()->IsExtensionAllowedByPolicy(
3463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      extension_->id())) {
3473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    ReportFailureFromUIThread("This extension is blacklisted by admin policy.");
3483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
3493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
3503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GURL overlapping_url;
352513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  const Extension* overlapping_extension =
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frontend_->GetExtensionByOverlappingWebExtent(extension_->web_extent());
35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (overlapping_extension &&
35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      overlapping_extension->id() != extension_->id()) {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ReportFailureFromUIThread(l10n_util::GetStringFUTF8(
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        UTF8ToUTF16(overlapping_extension->name())));
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_version_ =
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frontend_->extension_prefs()->GetVersionString(extension_->id());
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // First see if it's whitelisted by id (the old mechanism).
366513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  bool whitelisted = ClearWhitelistedInstallId(extension_->id()) &&
3674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      extension_->plugins().empty() && is_gallery_install_;
368513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Now check if it's whitelisted by manifest.
370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<DictionaryValue> whitelisted_manifest(
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      RemoveWhitelistedManifest(extension_->id()));
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (is_gallery_install_ && whitelisted_manifest.get()) {
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!EqualsIgnoringPublicKey(*extension_->manifest_value(),
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                 *whitelisted_manifest)) {
375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ReportFailureFromUIThread(
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID));
377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    whitelisted = true;
380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
3823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (client_ &&
383513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      (!allow_silent_install_ || !whitelisted)) {
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AddRef();  // Balanced in Proceed() and Abort().
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    client_->ConfirmInstall(this, extension_.get());
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
387731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    BrowserThread::PostTask(
388731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::FILE, FROM_HERE,
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NewRunnableMethod(this, &CrxInstaller::CompleteInstall));
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return;
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CrxInstaller::InstallUIProceed() {
395731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
396731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        BrowserThread::FILE, FROM_HERE,
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NewRunnableMethod(this, &CrxInstaller::CompleteInstall));
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Release();  // balanced in ConfirmInstall().
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::InstallUIAbort() {
403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Technically, this can be called for other reasons than the user hitting
404ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // cancel, but they're rare.
405ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ExtensionService::RecordPermissionMessagesHistogram(
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      extension_, "Extensions.Permissions_InstallCancel");
407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Kill the theme loading bubble.
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NotificationService* service = NotificationService::current();
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  service->Notify(NotificationType::NO_THEME_DETECTED,
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  Source<CrxInstaller>(this),
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  NotificationService::NoDetails());
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Release();  // balanced in ConfirmInstall().
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We're done. Since we don't post any more tasks to ourself, our ref count
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // should go to zero and we die. The destructor will clean up the temp dir.
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::CompleteInstall() {
420731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!current_version_.empty()) {
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scoped_ptr<Version> current_version(
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        Version::GetVersionFromString(current_version_));
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (current_version->CompareTo(*(extension_->version())) > 0) {
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ReportFailureFromFileThread("Attempted to downgrade extension.");
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // See how long extension install paths are.  This is important on
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // windows, because file operations may fail if the path to a file
433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // exceeds a small constant.  See crbug.com/69693 .
434ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UMA_HISTOGRAM_CUSTOM_COUNTS(
435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    "Extensions.CrxInstallDirPathLength",
436ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        install_directory_.value().length(), 0, 500, 100);
437ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath version_dir = extension_file_util::InstallExtension(
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      unpacked_extension_root_,
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      extension_->id(),
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      extension_->VersionString(),
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      install_directory_);
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (version_dir.empty()) {
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ReportFailureFromFileThread(
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        l10n_util::GetStringUTF8(
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED));
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This is lame, but we must reload the extension because absolute paths
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // inside the content scripts are established inside InitFromValue() and we
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // just moved the extension.
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(aa): All paths to resources inside extensions should be created
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // lazily and based on the Extension's root path at that moment.
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string error;
456513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  extension_ = extension_file_util::LoadExtension(
457dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      version_dir,
458dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      install_source_,
459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Extension::REQUIRE_KEY,
460dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      &error);
461201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  CHECK(error.empty()) << error;
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ReportSuccessFromFileThread();
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportFailureFromFileThread(const std::string& error) {
467731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
468731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
469731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI, FROM_HERE,
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this, &CrxInstaller::ReportFailureFromUIThread, error));
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportFailureFromUIThread(const std::string& error) {
474731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NotificationService* service = NotificationService::current();
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  service->Notify(NotificationType::EXTENSION_INSTALL_ERROR,
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  Source<CrxInstaller>(this),
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  Details<const std::string>(&error));
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This isn't really necessary, it is only used because unit tests expect to
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // see errors get reported via this interface.
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(aa): Need to go through unit tests and clean them up too, probably get
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // rid of this line.
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ExtensionErrorReporter::GetInstance()->ReportError(error, false);  // quiet
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (client_)
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    client_->OnInstallFailure(error);
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportSuccessFromFileThread() {
493731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
494731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BrowserThread::PostTask(
495731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      BrowserThread::UI, FROM_HERE,
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this, &CrxInstaller::ReportSuccessFromUIThread));
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrxInstaller::ReportSuccessFromUIThread() {
500731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If there is a client, tell the client about installation.
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (client_)
504ac1e49eb6695f711d72215fcdf9388548942a00dBen Murdoch    client_->OnInstallSuccess(extension_.get(), install_icon_.get());
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Tell the frontend about the installation and hand off ownership of
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // extension_ to it.
508201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  frontend_->OnExtensionInstalled(extension_);
509513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  extension_ = NULL;
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We're done. We don't post any more tasks to ourselves so we are deleted
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // soon.
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
514