Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / startup_helper.cc
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.
4
5 #include "chrome/browser/extensions/startup_helper.h"
6
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/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"
27
28 #if defined(OS_WIN)
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"
33 #endif
34
35 using content::BrowserThread;
36
37 namespace extensions {
38
39 namespace {
40
41 void PrintPackExtensionMessage(const std::string& message) {
42   VLOG(1) << message;
43 }
44
45 // On Windows, the jumplist action for installing an ephemeral app has to use
46 // the --install-ephemeral-app-from-webstore command line arg to initiate an
47 // install.
48 scoped_refptr<WebstoreStandaloneInstaller> CreateEphemeralAppInstaller(
49     Profile* profile,
50     const std::string& app_id,
51     WebstoreStandaloneInstaller::Callback callback) {
52   scoped_refptr<WebstoreStandaloneInstaller> installer;
53
54 #if defined(OS_WIN)
55   ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
56   DCHECK(registry);
57   if (!registry->GetExtensionById(app_id, ExtensionRegistry::EVERYTHING) ||
58       !util::IsEphemeralApp(app_id, profile)) {
59     return installer;
60   }
61
62   AppWindowRegistry* app_window_registry = AppWindowRegistry::Get(profile);
63   DCHECK(app_window_registry);
64   AppWindow* app_window =
65       app_window_registry->GetCurrentAppWindowForApp(app_id);
66   if (!app_window)
67     return installer;
68
69   installer = new WebstoreInstallWithPrompt(
70       app_id, profile, app_window->GetNativeWindow(), callback);
71 #endif
72
73   return installer;
74 }
75
76 }  // namespace
77
78 StartupHelper::StartupHelper() : pack_job_succeeded_(false) {
79   ExtensionsClient::Set(ChromeExtensionsClient::GetInstance());
80 }
81
82 void StartupHelper::OnPackSuccess(
83     const base::FilePath& crx_path,
84     const base::FilePath& output_private_key_path) {
85   pack_job_succeeded_ = true;
86   PrintPackExtensionMessage(
87       base::UTF16ToUTF8(
88           PackExtensionJob::StandardSuccessMessage(crx_path,
89                                                    output_private_key_path)));
90 }
91
92 void StartupHelper::OnPackFailure(const std::string& error_message,
93                                   ExtensionCreator::ErrorType type) {
94   PrintPackExtensionMessage(error_message);
95 }
96
97 bool StartupHelper::PackExtension(const CommandLine& cmd_line) {
98   if (!cmd_line.HasSwitch(switches::kPackExtension))
99     return false;
100
101   // Input Paths.
102   base::FilePath src_dir =
103       cmd_line.GetSwitchValuePath(switches::kPackExtension);
104   base::FilePath private_key_path;
105   if (cmd_line.HasSwitch(switches::kPackExtensionKey)) {
106     private_key_path = cmd_line.GetSwitchValuePath(switches::kPackExtensionKey);
107   }
108
109   // Launch a job to perform the packing on the file thread.  Ignore warnings
110   // from the packing process. (e.g. Overwrite any existing crx file.)
111   pack_job_ = new PackExtensionJob(this, src_dir, private_key_path,
112                                    ExtensionCreator::kOverwriteCRX);
113   pack_job_->set_asynchronous(false);
114   pack_job_->Start();
115
116   return pack_job_succeeded_;
117 }
118
119 namespace {
120
121 class ValidateCrxHelper : public SandboxedUnpackerClient {
122  public:
123   ValidateCrxHelper(const base::FilePath& crx_file,
124                     const base::FilePath& temp_dir,
125                     base::RunLoop* run_loop)
126       : crx_file_(crx_file), temp_dir_(temp_dir), run_loop_(run_loop),
127         finished_(false), success_(false) {}
128
129   bool finished() { return finished_; }
130   bool success() { return success_; }
131   const base::string16& error() { return error_; }
132
133   void Start() {
134     BrowserThread::PostTask(BrowserThread::FILE,
135                             FROM_HERE,
136                             base::Bind(&ValidateCrxHelper::StartOnFileThread,
137                                        this));
138   }
139
140  protected:
141   virtual ~ValidateCrxHelper() {}
142
143   virtual void OnUnpackSuccess(const base::FilePath& temp_dir,
144                                const base::FilePath& extension_root,
145                                const base::DictionaryValue* original_manifest,
146                                const Extension* extension,
147                                const SkBitmap& install_icon) OVERRIDE {
148     finished_ = true;
149     success_ = true;
150     BrowserThread::PostTask(BrowserThread::UI,
151                             FROM_HERE,
152                             base::Bind(&ValidateCrxHelper::FinishOnUIThread,
153                                        this));
154   }
155
156   virtual void OnUnpackFailure(const base::string16& error) OVERRIDE {
157     finished_ = true;
158     success_ = false;
159     error_ = error;
160     BrowserThread::PostTask(BrowserThread::UI,
161                             FROM_HERE,
162                             base::Bind(&ValidateCrxHelper::FinishOnUIThread,
163                                        this));
164   }
165
166   void FinishOnUIThread() {
167     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168     if (run_loop_->running())
169       run_loop_->Quit();
170   }
171
172   void StartOnFileThread() {
173     CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
174     scoped_refptr<base::MessageLoopProxy> file_thread_proxy =
175         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
176
177     scoped_refptr<SandboxedUnpacker> unpacker(
178         new SandboxedUnpacker(crx_file_,
179                               Manifest::INTERNAL,
180                               0, /* no special creation flags */
181                               temp_dir_,
182                               file_thread_proxy.get(),
183                               this));
184     unpacker->Start();
185   }
186
187   // The file being validated.
188   const base::FilePath& crx_file_;
189
190   // The temporary directory where the sandboxed unpacker will do work.
191   const base::FilePath& temp_dir_;
192
193   // Unowned pointer to a runloop, so our consumer can wait for us to finish.
194   base::RunLoop* run_loop_;
195
196   // Whether we're finished unpacking;
197   bool finished_;
198
199   // Whether the unpacking was successful.
200   bool success_;
201
202   // If the unpacking wasn't successful, this contains an error message.
203   base::string16 error_;
204 };
205
206 }  // namespace
207
208 bool StartupHelper::ValidateCrx(const CommandLine& cmd_line,
209                                 std::string* error) {
210   CHECK(error);
211   base::FilePath path = cmd_line.GetSwitchValuePath(switches::kValidateCrx);
212   if (path.empty()) {
213     *error = base::StringPrintf("Empty path passed for %s",
214                                 switches::kValidateCrx);
215     return false;
216   }
217   base::ScopedTempDir temp_dir;
218
219   if (!temp_dir.CreateUniqueTempDir()) {
220     *error = std::string("Failed to create temp dir");
221     return false;
222   }
223
224   base::RunLoop run_loop;
225   scoped_refptr<ValidateCrxHelper> helper(
226       new ValidateCrxHelper(path, temp_dir.path(), &run_loop));
227   helper->Start();
228   if (!helper->finished())
229     run_loop.Run();
230
231   bool success = helper->success();
232   if (!success)
233     *error = base::UTF16ToUTF8(helper->error());
234   return success;
235 }
236
237 namespace {
238
239 class AppInstallHelper {
240  public:
241   // A callback for when the install process is done.
242   typedef base::Callback<void()> DoneCallback;
243
244   AppInstallHelper();
245   virtual ~AppInstallHelper();
246   bool success() { return success_; }
247   const std::string& error() { return error_; }
248   void BeginInstall(Profile* profile,
249                     const std::string& id,
250                     bool show_prompt,
251                     DoneCallback callback);
252
253  private:
254   WebstoreStandaloneInstaller::Callback Callback();
255   void OnAppInstallComplete(bool success,
256                             const std::string& error,
257                             webstore_install::Result result);
258
259   DoneCallback done_callback_;
260
261   // These hold on to the result of the app install when it is complete.
262   bool success_;
263   std::string error_;
264
265   scoped_refptr<WebstoreStandaloneInstaller> installer_;
266 };
267
268 AppInstallHelper::AppInstallHelper() : success_(false) {}
269
270 AppInstallHelper::~AppInstallHelper() {}
271
272 WebstoreStandaloneInstaller::Callback AppInstallHelper::Callback() {
273   return base::Bind(&AppInstallHelper::OnAppInstallComplete,
274                     base::Unretained(this));
275 }
276
277 void AppInstallHelper::BeginInstall(
278     Profile* profile,
279     const std::string& id,
280     bool show_prompt,
281     DoneCallback done_callback) {
282   done_callback_ = done_callback;
283
284   WebstoreStandaloneInstaller::Callback callback =
285       base::Bind(&AppInstallHelper::OnAppInstallComplete,
286                  base::Unretained(this));
287
288   installer_ = CreateEphemeralAppInstaller(profile, id, callback);
289   if (installer_.get()) {
290     installer_->BeginInstall();
291   } else {
292     error_ = "Not a supported ephemeral app installation.";
293     done_callback_.Run();
294   }
295 }
296
297 void AppInstallHelper::OnAppInstallComplete(bool success,
298                                             const std::string& error,
299                                             webstore_install::Result result) {
300   success_ = success;
301   error_= error;
302   done_callback_.Run();
303 }
304
305 }  // namespace
306
307 bool StartupHelper::InstallEphemeralApp(const CommandLine& cmd_line,
308                                         Profile* profile) {
309   std::string id =
310       cmd_line.GetSwitchValueASCII(switches::kInstallEphemeralAppFromWebstore);
311   if (!crx_file::id_util::IdIsValid(id)) {
312     LOG(ERROR) << "Invalid id for "
313         << switches::kInstallEphemeralAppFromWebstore << " : '" << id << "'";
314     return false;
315   }
316
317   AppInstallHelper helper;
318   base::RunLoop run_loop;
319   helper.BeginInstall(profile, id, true, run_loop.QuitClosure());
320   run_loop.Run();
321
322   if (!helper.success())
323     LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error();
324   return helper.success();
325 }
326
327 StartupHelper::~StartupHelper() {
328   if (pack_job_.get())
329     pack_job_->ClearClient();
330 }
331
332 }  // namespace extensions