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/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"
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"
36 using content::BrowserThread
;
38 namespace extensions
{
42 void PrintPackExtensionMessage(const std::string
& 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
49 scoped_refptr
<WebstoreStandaloneInstaller
> CreateEphemeralAppInstaller(
51 const std::string
& app_id
,
52 WebstoreStandaloneInstaller::Callback callback
) {
53 scoped_refptr
<WebstoreStandaloneInstaller
> installer
;
56 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
58 if (!registry
->GetExtensionById(app_id
, ExtensionRegistry::EVERYTHING
) ||
59 !util::IsEphemeralApp(app_id
, profile
)) {
63 AppWindowRegistry
* app_window_registry
= AppWindowRegistry::Get(profile
);
64 DCHECK(app_window_registry
);
65 AppWindow
* app_window
=
66 app_window_registry
->GetCurrentAppWindowForApp(app_id
);
70 installer
= new WebstoreInstallWithPrompt(
71 app_id
, profile
, app_window
->GetNativeWindow(), callback
);
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(
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
))
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);
117 return pack_job_succeeded_
;
122 class ValidateCrxHelper
: public SandboxedUnpackerClient
{
124 ValidateCrxHelper(const CRXFileInfo
& file
,
125 const base::FilePath
& temp_dir
,
126 base::RunLoop
* run_loop
)
133 bool finished() { return finished_
; }
134 bool success() { return success_
; }
135 const base::string16
& error() { return error_
; }
138 BrowserThread::PostTask(BrowserThread::FILE,
140 base::Bind(&ValidateCrxHelper::StartOnFileThread
,
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
{
154 BrowserThread::PostTask(BrowserThread::UI
,
156 base::Bind(&ValidateCrxHelper::FinishOnUIThread
,
160 void OnUnpackFailure(const CrxInstallError
& error
) override
{
163 error_
= error
.message();
164 BrowserThread::PostTask(BrowserThread::UI
,
166 base::Bind(&ValidateCrxHelper::FinishOnUIThread
,
170 void FinishOnUIThread() {
171 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
172 if (run_loop_
->running())
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_
,
184 0, /* no special creation flags */
186 file_task_runner
.get(),
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;
203 // Whether the unpacking was successful.
206 // If the unpacking wasn't successful, this contains an error message.
207 base::string16 error_
;
212 bool StartupHelper::ValidateCrx(const base::CommandLine
& cmd_line
,
213 std::string
* error
) {
215 base::FilePath path
= cmd_line
.GetSwitchValuePath(switches::kValidateCrx
);
217 *error
= base::StringPrintf("Empty path passed for %s",
218 switches::kValidateCrx
);
221 base::ScopedTempDir temp_dir
;
223 if (!temp_dir
.CreateUniqueTempDir()) {
224 *error
= std::string("Failed to create temp dir");
228 base::RunLoop run_loop
;
229 CRXFileInfo
file(path
);
230 scoped_refptr
<ValidateCrxHelper
> helper(
231 new ValidateCrxHelper(file
, temp_dir
.path(), &run_loop
));
233 if (!helper
->finished())
236 bool success
= helper
->success();
238 *error
= base::UTF16ToUTF8(helper
->error());
244 class AppInstallHelper
{
246 // A callback for when the install process is done.
247 typedef base::Callback
<void()> DoneCallback
;
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
,
256 DoneCallback callback
);
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.
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(
284 const std::string
& id
,
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();
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
) {
307 done_callback_
.Run();
312 bool StartupHelper::InstallEphemeralApp(const base::CommandLine
& cmd_line
,
315 cmd_line
.GetSwitchValueASCII(switches::kInstallEphemeralAppFromWebstore
);
316 if (!crx_file::id_util::IdIsValid(id
)) {
317 LOG(ERROR
) << "Invalid id for "
318 << switches::kInstallEphemeralAppFromWebstore
<< " : '" << id
<< "'";
322 AppInstallHelper helper
;
323 base::RunLoop run_loop
;
324 helper
.BeginInstall(profile
, id
, true, run_loop
.QuitClosure());
327 if (!helper
.success())
328 LOG(ERROR
) << "InstallFromWebstore failed with error: " << helper
.error();
329 return helper
.success();
332 StartupHelper::~StartupHelper() {
334 pack_job_
->ClearClient();
337 } // namespace extensions