Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / startup_helper.cc
blobc435799a1973f1b9fea0afe0f7a4f28736e343e8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/startup_helper.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/webstore_startup_installer.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/chrome_extensions_client.h"
22 #include "components/crx_file/id_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/web_contents.h"
25 #include "extensions/browser/sandboxed_unpacker.h"
26 #include "extensions/common/extension.h"
27 #include "ipc/ipc_message.h"
29 #if defined(OS_WIN)
30 #include "extensions/browser/app_window/app_window.h"
31 #include "extensions/browser/app_window/app_window_registry.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "extensions/browser/extension_util.h"
34 #endif
36 using content::BrowserThread;
38 namespace extensions {
40 namespace {
42 void PrintPackExtensionMessage(const std::string& message) {
43 VLOG(1) << message;
46 // On Windows, the jumplist action for installing an ephemeral app has to use
47 // the --install-ephemeral-app-from-webstore command line arg to initiate an
48 // install.
49 scoped_refptr<WebstoreStandaloneInstaller> CreateEphemeralAppInstaller(
50 Profile* profile,
51 const std::string& app_id,
52 WebstoreStandaloneInstaller::Callback callback) {
53 scoped_refptr<WebstoreStandaloneInstaller> installer;
55 #if defined(OS_WIN)
56 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
57 DCHECK(registry);
58 if (!registry->GetExtensionById(app_id, ExtensionRegistry::EVERYTHING) ||
59 !util::IsEphemeralApp(app_id, profile)) {
60 return installer;
63 AppWindowRegistry* app_window_registry = AppWindowRegistry::Get(profile);
64 DCHECK(app_window_registry);
65 AppWindow* app_window =
66 app_window_registry->GetCurrentAppWindowForApp(app_id);
67 if (!app_window)
68 return installer;
70 installer = new WebstoreInstallWithPrompt(
71 app_id, profile, app_window->GetNativeWindow(), callback);
72 #endif
74 return installer;
77 } // namespace
79 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
80 ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
83 void StartupHelper::OnPackSuccess(
84 const base::FilePath& crx_path,
85 const base::FilePath& output_private_key_path) {
86 pack_job_succeeded_ = true;
87 PrintPackExtensionMessage(
88 base::UTF16ToUTF8(
89 PackExtensionJob::StandardSuccessMessage(crx_path,
90 output_private_key_path)));
93 void StartupHelper::OnPackFailure(const std::string& error_message,
94 ExtensionCreator::ErrorType type) {
95 PrintPackExtensionMessage(error_message);
98 bool StartupHelper::PackExtension(const base::CommandLine& cmd_line) {
99 if (!cmd_line.HasSwitch(switches::kPackExtension))
100 return false;
102 // Input Paths.
103 base::FilePath src_dir =
104 cmd_line.GetSwitchValuePath(switches::kPackExtension);
105 base::FilePath private_key_path;
106 if (cmd_line.HasSwitch(switches::kPackExtensionKey)) {
107 private_key_path = cmd_line.GetSwitchValuePath(switches::kPackExtensionKey);
110 // Launch a job to perform the packing on the file thread. Ignore warnings
111 // from the packing process. (e.g. Overwrite any existing crx file.)
112 pack_job_ = new PackExtensionJob(this, src_dir, private_key_path,
113 ExtensionCreator::kOverwriteCRX);
114 pack_job_->set_asynchronous(false);
115 pack_job_->Start();
117 return pack_job_succeeded_;
120 namespace {
122 class ValidateCrxHelper : public SandboxedUnpackerClient {
123 public:
124 ValidateCrxHelper(const CRXFileInfo& file,
125 const base::FilePath& temp_dir,
126 base::RunLoop* run_loop)
127 : crx_file_(file),
128 temp_dir_(temp_dir),
129 run_loop_(run_loop),
130 finished_(false),
131 success_(false) {}
133 bool finished() { return finished_; }
134 bool success() { return success_; }
135 const base::string16& error() { return error_; }
137 void Start() {
138 BrowserThread::PostTask(BrowserThread::FILE,
139 FROM_HERE,
140 base::Bind(&ValidateCrxHelper::StartOnFileThread,
141 this));
144 protected:
145 ~ValidateCrxHelper() override {}
147 void OnUnpackSuccess(const base::FilePath& temp_dir,
148 const base::FilePath& extension_root,
149 const base::DictionaryValue* original_manifest,
150 const Extension* extension,
151 const SkBitmap& install_icon) override {
152 finished_ = true;
153 success_ = true;
154 BrowserThread::PostTask(BrowserThread::UI,
155 FROM_HERE,
156 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
157 this));
160 void OnUnpackFailure(const CrxInstallError& error) override {
161 finished_ = true;
162 success_ = false;
163 error_ = error.message();
164 BrowserThread::PostTask(BrowserThread::UI,
165 FROM_HERE,
166 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
167 this));
170 void FinishOnUIThread() {
171 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172 if (run_loop_->running())
173 run_loop_->Quit();
176 void StartOnFileThread() {
177 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
178 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner =
179 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
181 scoped_refptr<SandboxedUnpacker> unpacker(
182 new SandboxedUnpacker(crx_file_,
183 Manifest::INTERNAL,
184 0, /* no special creation flags */
185 temp_dir_,
186 file_task_runner.get(),
187 this));
188 unpacker->Start();
191 // The file being validated.
192 const CRXFileInfo& crx_file_;
194 // The temporary directory where the sandboxed unpacker will do work.
195 const base::FilePath& temp_dir_;
197 // Unowned pointer to a runloop, so our consumer can wait for us to finish.
198 base::RunLoop* run_loop_;
200 // Whether we're finished unpacking;
201 bool finished_;
203 // Whether the unpacking was successful.
204 bool success_;
206 // If the unpacking wasn't successful, this contains an error message.
207 base::string16 error_;
210 } // namespace
212 bool StartupHelper::ValidateCrx(const base::CommandLine& cmd_line,
213 std::string* error) {
214 CHECK(error);
215 base::FilePath path = cmd_line.GetSwitchValuePath(switches::kValidateCrx);
216 if (path.empty()) {
217 *error = base::StringPrintf("Empty path passed for %s",
218 switches::kValidateCrx);
219 return false;
221 base::ScopedTempDir temp_dir;
223 if (!temp_dir.CreateUniqueTempDir()) {
224 *error = std::string("Failed to create temp dir");
225 return false;
228 base::RunLoop run_loop;
229 CRXFileInfo file(path);
230 scoped_refptr<ValidateCrxHelper> helper(
231 new ValidateCrxHelper(file, temp_dir.path(), &run_loop));
232 helper->Start();
233 if (!helper->finished())
234 run_loop.Run();
236 bool success = helper->success();
237 if (!success)
238 *error = base::UTF16ToUTF8(helper->error());
239 return success;
242 namespace {
244 class AppInstallHelper {
245 public:
246 // A callback for when the install process is done.
247 typedef base::Callback<void()> DoneCallback;
249 AppInstallHelper();
250 virtual ~AppInstallHelper();
251 bool success() { return success_; }
252 const std::string& error() { return error_; }
253 void BeginInstall(Profile* profile,
254 const std::string& id,
255 bool show_prompt,
256 DoneCallback callback);
258 private:
259 WebstoreStandaloneInstaller::Callback Callback();
260 void OnAppInstallComplete(bool success,
261 const std::string& error,
262 webstore_install::Result result);
264 DoneCallback done_callback_;
266 // These hold on to the result of the app install when it is complete.
267 bool success_;
268 std::string error_;
270 scoped_refptr<WebstoreStandaloneInstaller> installer_;
273 AppInstallHelper::AppInstallHelper() : success_(false) {}
275 AppInstallHelper::~AppInstallHelper() {}
277 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() {
278 return base::Bind(&AppInstallHelper::OnAppInstallComplete,
279 base::Unretained(this));
282 void AppInstallHelper::BeginInstall(
283 Profile* profile,
284 const std::string& id,
285 bool show_prompt,
286 DoneCallback done_callback) {
287 done_callback_ = done_callback;
289 WebstoreStandaloneInstaller::Callback callback =
290 base::Bind(&AppInstallHelper::OnAppInstallComplete,
291 base::Unretained(this));
293 installer_ = CreateEphemeralAppInstaller(profile, id, callback);
294 if (installer_.get()) {
295 installer_->BeginInstall();
296 } else {
297 error_ = "Not a supported ephemeral app installation.";
298 done_callback_.Run();
302 void AppInstallHelper::OnAppInstallComplete(bool success,
303 const std::string& error,
304 webstore_install::Result result) {
305 success_ = success;
306 error_ = error;
307 done_callback_.Run();
310 } // namespace
312 bool StartupHelper::InstallEphemeralApp(const base::CommandLine& cmd_line,
313 Profile* profile) {
314 std::string id =
315 cmd_line.GetSwitchValueASCII(switches::kInstallEphemeralAppFromWebstore);
316 if (!crx_file::id_util::IdIsValid(id)) {
317 LOG(ERROR) << "Invalid id for "
318 << switches::kInstallEphemeralAppFromWebstore << " : '" << id << "'";
319 return false;
322 AppInstallHelper helper;
323 base::RunLoop run_loop;
324 helper.BeginInstall(profile, id, true, run_loop.QuitClosure());
325 run_loop.Run();
327 if (!helper.success())
328 LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error();
329 return helper.success();
332 StartupHelper::~StartupHelper() {
333 if (pack_job_.get())
334 pack_job_->ClearClient();
337 } // namespace extensions