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"
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/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/sandboxed_unpacker.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/common/extension.h"
26 #include "ipc/ipc_message.h"
29 #include "extensions/browser/app_window/app_window.h"
30 #include "extensions/browser/app_window/app_window_registry.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/extension_util.h"
35 using content::BrowserThread;
39 void PrintPackExtensionMessage(const std::string& message) {
43 // On Windows, the jumplist action for installing an ephemeral app has to use
44 // the --install-from-webstore command line arg to initiate an install.
45 scoped_refptr<extensions::WebstoreStandaloneInstaller>
46 CreateEphemeralAppInstaller(
48 const std::string& app_id,
49 extensions::WebstoreStandaloneInstaller::Callback callback) {
50 scoped_refptr<extensions::WebstoreStandaloneInstaller> installer;
53 using extensions::ExtensionRegistry;
54 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
56 if (!registry->GetExtensionById(app_id, ExtensionRegistry::EVERYTHING) ||
57 !extensions::util::IsEphemeralApp(app_id, profile)) {
61 extensions::AppWindowRegistry* app_window_registry =
62 extensions::AppWindowRegistry::Get(profile);
63 DCHECK(app_window_registry);
64 extensions::AppWindow* app_window =
65 app_window_registry->GetCurrentAppWindowForApp(app_id);
69 installer = new extensions::WebstoreInstallWithPrompt(
70 app_id, profile, app_window->GetNativeWindow(), callback);
78 namespace extensions {
80 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
81 ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
84 void StartupHelper::OnPackSuccess(
85 const base::FilePath& crx_path,
86 const base::FilePath& output_private_key_path) {
87 pack_job_succeeded_ = true;
88 PrintPackExtensionMessage(
90 PackExtensionJob::StandardSuccessMessage(crx_path,
91 output_private_key_path)));
94 void StartupHelper::OnPackFailure(const std::string& error_message,
95 ExtensionCreator::ErrorType type) {
96 PrintPackExtensionMessage(error_message);
99 bool StartupHelper::PackExtension(const CommandLine& cmd_line) {
100 if (!cmd_line.HasSwitch(switches::kPackExtension))
104 base::FilePath src_dir =
105 cmd_line.GetSwitchValuePath(switches::kPackExtension);
106 base::FilePath private_key_path;
107 if (cmd_line.HasSwitch(switches::kPackExtensionKey)) {
108 private_key_path = cmd_line.GetSwitchValuePath(switches::kPackExtensionKey);
111 // Launch a job to perform the packing on the file thread. Ignore warnings
112 // from the packing process. (e.g. Overwrite any existing crx file.)
113 pack_job_ = new PackExtensionJob(this, src_dir, private_key_path,
114 ExtensionCreator::kOverwriteCRX);
115 pack_job_->set_asynchronous(false);
118 return pack_job_succeeded_;
123 class ValidateCrxHelper : public SandboxedUnpackerClient {
125 ValidateCrxHelper(const base::FilePath& crx_file,
126 const base::FilePath& temp_dir,
127 base::RunLoop* run_loop)
128 : crx_file_(crx_file), temp_dir_(temp_dir), run_loop_(run_loop),
129 finished_(false), success_(false) {}
131 bool finished() { return finished_; }
132 bool success() { return success_; }
133 const base::string16& error() { return error_; }
136 BrowserThread::PostTask(BrowserThread::FILE,
138 base::Bind(&ValidateCrxHelper::StartOnFileThread,
143 virtual ~ValidateCrxHelper() {}
145 virtual void OnUnpackSuccess(const base::FilePath& temp_dir,
146 const base::FilePath& extension_root,
147 const base::DictionaryValue* original_manifest,
148 const Extension* extension,
149 const SkBitmap& install_icon) OVERRIDE {
152 BrowserThread::PostTask(BrowserThread::UI,
154 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
158 virtual void OnUnpackFailure(const base::string16& error) OVERRIDE {
162 BrowserThread::PostTask(BrowserThread::UI,
164 base::Bind(&ValidateCrxHelper::FinishOnUIThread,
168 void FinishOnUIThread() {
169 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
170 if (run_loop_->running())
174 void StartOnFileThread() {
175 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
176 scoped_refptr<base::MessageLoopProxy> file_thread_proxy =
177 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
179 scoped_refptr<SandboxedUnpacker> unpacker(
180 new SandboxedUnpacker(crx_file_,
182 0, /* no special creation flags */
184 file_thread_proxy.get(),
189 // The file being validated.
190 const base::FilePath& crx_file_;
192 // The temporary directory where the sandboxed unpacker will do work.
193 const base::FilePath& temp_dir_;
195 // Unowned pointer to a runloop, so our consumer can wait for us to finish.
196 base::RunLoop* run_loop_;
198 // Whether we're finished unpacking;
201 // Whether the unpacking was successful.
204 // If the unpacking wasn't successful, this contains an error message.
205 base::string16 error_;
210 bool StartupHelper::ValidateCrx(const CommandLine& cmd_line,
211 std::string* error) {
213 base::FilePath path = cmd_line.GetSwitchValuePath(switches::kValidateCrx);
215 *error = base::StringPrintf("Empty path passed for %s",
216 switches::kValidateCrx);
219 base::ScopedTempDir temp_dir;
221 if (!temp_dir.CreateUniqueTempDir()) {
222 *error = std::string("Failed to create temp dir");
226 base::RunLoop run_loop;
227 scoped_refptr<ValidateCrxHelper> helper(
228 new ValidateCrxHelper(path, temp_dir.path(), &run_loop));
230 if (!helper->finished())
233 bool success = helper->success();
235 *error = base::UTF16ToUTF8(helper->error());
241 class AppInstallHelper {
243 // A callback for when the install process is done.
244 typedef base::Callback<void()> DoneCallback;
247 virtual ~AppInstallHelper();
248 bool success() { return success_; }
249 const std::string& error() { return error_; }
250 void BeginInstall(Profile* profile,
251 const std::string& id,
253 DoneCallback callback);
256 WebstoreStandaloneInstaller::Callback Callback();
257 void OnAppInstallComplete(bool success,
258 const std::string& error,
259 webstore_install::Result result);
261 DoneCallback done_callback_;
263 // These hold on to the result of the app install when it is complete.
267 scoped_refptr<WebstoreStandaloneInstaller> installer_;
270 AppInstallHelper::AppInstallHelper() : success_(false) {}
272 AppInstallHelper::~AppInstallHelper() {}
274 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() {
275 return base::Bind(&AppInstallHelper::OnAppInstallComplete,
276 base::Unretained(this));
279 void AppInstallHelper::BeginInstall(
281 const std::string& id,
283 DoneCallback done_callback) {
284 done_callback_ = done_callback;
286 WebstoreStandaloneInstaller::Callback callback =
287 base::Bind(&AppInstallHelper::OnAppInstallComplete,
288 base::Unretained(this));
290 installer_ = CreateEphemeralAppInstaller(profile, id, callback);
291 if (!installer_.get()) {
293 new WebstoreStartupInstaller(id, profile, show_prompt, callback);
295 installer_->BeginInstall();
298 void AppInstallHelper::OnAppInstallComplete(bool success,
299 const std::string& error,
300 webstore_install::Result result) {
303 done_callback_.Run();
306 void DeleteHelperAndRunCallback(AppInstallHelper* helper,
307 base::Callback<void()> callback) {
314 bool StartupHelper::InstallFromWebstore(const CommandLine& cmd_line,
316 std::string id = cmd_line.GetSwitchValueASCII(switches::kInstallFromWebstore);
317 if (!crx_file::id_util::IdIsValid(id)) {
318 LOG(ERROR) << "Invalid id for " << switches::kInstallFromWebstore
319 << " : '" << id << "'";
323 AppInstallHelper helper;
324 base::RunLoop run_loop;
325 helper.BeginInstall(profile, id, true, run_loop.QuitClosure());
328 if (!helper.success())
329 LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error();
330 return helper.success();
333 void StartupHelper::LimitedInstallFromWebstore(
334 const CommandLine& cmd_line,
336 base::Callback<void()> done_callback) {
337 std::string id = WebStoreIdFromLimitedInstallCmdLine(cmd_line);
338 if (!crx_file::id_util::IdIsValid(id)) {
339 LOG(ERROR) << "Invalid index for " << switches::kLimitedInstallFromWebstore;
344 AppInstallHelper* helper = new AppInstallHelper();
345 helper->BeginInstall(profile, id, false /*show_prompt*/,
346 base::Bind(&DeleteHelperAndRunCallback,
347 helper, done_callback));
350 std::string StartupHelper::WebStoreIdFromLimitedInstallCmdLine(
351 const CommandLine& cmd_line) {
352 std::string index = cmd_line.GetSwitchValueASCII(
353 switches::kLimitedInstallFromWebstore);
356 id = "nckgahadagoaajjgafhacjanaoiihapd";
357 } else if (index == "2") {
358 id = "ecglahbcnmdpdciemllbhojghbkagdje";
363 StartupHelper::~StartupHelper() {
365 pack_job_->ClearClient();
368 } // namespace extensions