Home | History | Annotate | Download | only in extensions
      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
    333