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 "content/public/browser/browser_thread.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/common/extension.h"
25 #include "ipc/ipc_message.h"
27 using content::BrowserThread
;
31 void PrintPackExtensionMessage(const std::string
& message
) {
32 printf("%s\n", message
.c_str());
37 namespace extensions
{
39 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
40 ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
43 void StartupHelper::OnPackSuccess(
44 const base::FilePath
& crx_path
,
45 const base::FilePath
& output_private_key_path
) {
46 pack_job_succeeded_
= true;
47 PrintPackExtensionMessage(
49 PackExtensionJob::StandardSuccessMessage(crx_path
,
50 output_private_key_path
)));
53 void StartupHelper::OnPackFailure(const std::string
& error_message
,
54 ExtensionCreator::ErrorType type
) {
55 PrintPackExtensionMessage(error_message
);
58 bool StartupHelper::PackExtension(const CommandLine
& cmd_line
) {
59 if (!cmd_line
.HasSwitch(switches::kPackExtension
))
63 base::FilePath src_dir
=
64 cmd_line
.GetSwitchValuePath(switches::kPackExtension
);
65 base::FilePath private_key_path
;
66 if (cmd_line
.HasSwitch(switches::kPackExtensionKey
)) {
67 private_key_path
= cmd_line
.GetSwitchValuePath(switches::kPackExtensionKey
);
70 // Launch a job to perform the packing on the file thread. Ignore warnings
71 // from the packing process. (e.g. Overwrite any existing crx file.)
72 pack_job_
= new PackExtensionJob(this, src_dir
, private_key_path
,
73 ExtensionCreator::kOverwriteCRX
);
74 pack_job_
->set_asynchronous(false);
77 return pack_job_succeeded_
;
82 class ValidateCrxHelper
: public SandboxedUnpackerClient
{
84 ValidateCrxHelper(const base::FilePath
& crx_file
,
85 const base::FilePath
& temp_dir
,
86 base::RunLoop
* run_loop
)
87 : crx_file_(crx_file
), temp_dir_(temp_dir
), run_loop_(run_loop
),
88 finished_(false), success_(false) {}
90 bool finished() { return finished_
; }
91 bool success() { return success_
; }
92 const base::string16
& error() { return error_
; }
95 BrowserThread::PostTask(BrowserThread::FILE,
97 base::Bind(&ValidateCrxHelper::StartOnFileThread
,
102 virtual ~ValidateCrxHelper() {}
104 virtual void OnUnpackSuccess(const base::FilePath
& temp_dir
,
105 const base::FilePath
& extension_root
,
106 const base::DictionaryValue
* original_manifest
,
107 const Extension
* extension
,
108 const SkBitmap
& install_icon
) OVERRIDE
{
111 BrowserThread::PostTask(BrowserThread::UI
,
113 base::Bind(&ValidateCrxHelper::FinishOnUIThread
,
117 virtual void OnUnpackFailure(const base::string16
& error
) OVERRIDE
{
121 BrowserThread::PostTask(BrowserThread::UI
,
123 base::Bind(&ValidateCrxHelper::FinishOnUIThread
,
127 void FinishOnUIThread() {
128 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
129 if (run_loop_
->running())
133 void StartOnFileThread() {
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
135 scoped_refptr
<base::MessageLoopProxy
> file_thread_proxy
=
136 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
138 scoped_refptr
<SandboxedUnpacker
> unpacker(
139 new SandboxedUnpacker(crx_file_
,
141 0, /* no special creation flags */
143 file_thread_proxy
.get(),
148 // The file being validated.
149 const base::FilePath
& crx_file_
;
151 // The temporary directory where the sandboxed unpacker will do work.
152 const base::FilePath
& temp_dir_
;
154 // Unowned pointer to a runloop, so our consumer can wait for us to finish.
155 base::RunLoop
* run_loop_
;
157 // Whether we're finished unpacking;
160 // Whether the unpacking was successful.
163 // If the unpacking wasn't successful, this contains an error message.
164 base::string16 error_
;
169 bool StartupHelper::ValidateCrx(const CommandLine
& cmd_line
,
170 std::string
* error
) {
172 base::FilePath path
= cmd_line
.GetSwitchValuePath(switches::kValidateCrx
);
174 *error
= base::StringPrintf("Empty path passed for %s",
175 switches::kValidateCrx
);
178 base::ScopedTempDir temp_dir
;
180 if (!temp_dir
.CreateUniqueTempDir()) {
181 *error
= std::string("Failed to create temp dir");
185 base::RunLoop run_loop
;
186 scoped_refptr
<ValidateCrxHelper
> helper(
187 new ValidateCrxHelper(path
, temp_dir
.path(), &run_loop
));
189 if (!helper
->finished())
192 bool success
= helper
->success();
194 *error
= base::UTF16ToUTF8(helper
->error());
200 class AppInstallHelper
{
202 // A callback for when the install process is done.
203 typedef base::Callback
<void()> DoneCallback
;
206 virtual ~AppInstallHelper();
207 bool success() { return success_
; }
208 const std::string
& error() { return error_
; }
209 void BeginInstall(Profile
* profile
,
210 const std::string
& id
,
212 DoneCallback callback
);
215 WebstoreStandaloneInstaller::Callback
Callback();
216 void OnAppInstallComplete(bool success
, const std::string
& error
);
218 DoneCallback done_callback_
;
220 // These hold on to the result of the app install when it is complete.
224 scoped_refptr
<WebstoreStandaloneInstaller
> installer_
;
227 AppInstallHelper::AppInstallHelper() : success_(false) {}
229 AppInstallHelper::~AppInstallHelper() {}
231 WebstoreStandaloneInstaller::Callback
AppInstallHelper::Callback() {
232 return base::Bind(&AppInstallHelper::OnAppInstallComplete
,
233 base::Unretained(this));
236 void AppInstallHelper::BeginInstall(
238 const std::string
& id
,
240 DoneCallback done_callback
) {
241 done_callback_
= done_callback
;
243 WebstoreStandaloneInstaller::Callback callback
=
244 base::Bind(&AppInstallHelper::OnAppInstallComplete
,
245 base::Unretained(this));
246 installer_
= new WebstoreStartupInstaller(
251 installer_
->BeginInstall();
254 void AppInstallHelper::OnAppInstallComplete(bool success
,
255 const std::string
& error
) {
258 done_callback_
.Run();
261 void DeleteHelperAndRunCallback(AppInstallHelper
* helper
,
262 base::Callback
<void()> callback
) {
269 bool StartupHelper::InstallFromWebstore(const CommandLine
& cmd_line
,
271 std::string id
= cmd_line
.GetSwitchValueASCII(switches::kInstallFromWebstore
);
272 if (!Extension::IdIsValid(id
)) {
273 LOG(ERROR
) << "Invalid id for " << switches::kInstallFromWebstore
274 << " : '" << id
<< "'";
278 AppInstallHelper helper
;
279 base::RunLoop run_loop
;
280 helper
.BeginInstall(profile
, id
, true, run_loop
.QuitClosure());
283 if (!helper
.success())
284 LOG(ERROR
) << "InstallFromWebstore failed with error: " << helper
.error();
285 return helper
.success();
288 void StartupHelper::LimitedInstallFromWebstore(
289 const CommandLine
& cmd_line
,
291 base::Callback
<void()> done_callback
) {
292 std::string id
= WebStoreIdFromLimitedInstallCmdLine(cmd_line
);
293 if (!Extension::IdIsValid(id
)) {
294 LOG(ERROR
) << "Invalid index for " << switches::kLimitedInstallFromWebstore
;
299 AppInstallHelper
* helper
= new AppInstallHelper();
300 helper
->BeginInstall(profile
, id
, false /*show_prompt*/,
301 base::Bind(&DeleteHelperAndRunCallback
,
302 helper
, done_callback
));
305 std::string
StartupHelper::WebStoreIdFromLimitedInstallCmdLine(
306 const CommandLine
& cmd_line
) {
307 std::string index
= cmd_line
.GetSwitchValueASCII(
308 switches::kLimitedInstallFromWebstore
);
311 id
= "nckgahadagoaajjgafhacjanaoiihapd";
312 } else if (index
== "2") {
313 id
= "ecglahbcnmdpdciemllbhojghbkagdje";
318 StartupHelper::~StartupHelper() {
320 pack_job_
->ClearClient();
323 } // namespace extensions