Home | History | Annotate | Download | only in apps
      1 // Copyright 2013 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/apps/ephemeral_app_launcher.h"
      6 
      7 #include "chrome/browser/extensions/extension_install_prompt.h"
      8 #include "chrome/browser/extensions/extension_util.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/ui/extensions/application_launch.h"
     11 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
     12 #include "content/public/browser/web_contents.h"
     13 #include "extensions/browser/extension_registry.h"
     14 #include "extensions/common/permissions/permissions_data.h"
     15 
     16 using content::WebContents;
     17 using extensions::Extension;
     18 using extensions::ExtensionRegistry;
     19 using extensions::WebstoreInstaller;
     20 
     21 namespace {
     22 
     23 const char kInvalidManifestError[] = "Invalid manifest";
     24 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted";
     25 const char kLaunchAbortedError[] = "Launch aborted";
     26 
     27 Profile* ProfileForWebContents(content::WebContents* contents) {
     28   if (!contents)
     29     return NULL;
     30 
     31   return Profile::FromBrowserContext(contents->GetBrowserContext());
     32 }
     33 
     34 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
     35   if (!contents)
     36     return NULL;
     37 
     38   return contents->GetTopLevelNativeWindow();
     39 }
     40 
     41 }  // namespace
     42 
     43 // static
     44 scoped_refptr<EphemeralAppLauncher>
     45 EphemeralAppLauncher::CreateForLauncher(
     46     const std::string& webstore_item_id,
     47     Profile* profile,
     48     gfx::NativeWindow parent_window,
     49     const Callback& callback) {
     50   scoped_refptr<EphemeralAppLauncher> installer =
     51       new EphemeralAppLauncher(webstore_item_id,
     52                                profile,
     53                                parent_window,
     54                                callback);
     55   installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
     56   return installer;
     57 }
     58 
     59 // static
     60 scoped_refptr<EphemeralAppLauncher>
     61 EphemeralAppLauncher::CreateForLink(
     62     const std::string& webstore_item_id,
     63     content::WebContents* web_contents) {
     64   scoped_refptr<EphemeralAppLauncher> installer =
     65       new EphemeralAppLauncher(webstore_item_id,
     66                                web_contents,
     67                                Callback());
     68   installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
     69   return installer;
     70 }
     71 
     72 void EphemeralAppLauncher::Start() {
     73   const Extension* extension =
     74       ExtensionRegistry::Get(profile())
     75           ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
     76   if (extension) {
     77     if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
     78                                                          profile())) {
     79       LaunchApp(extension);
     80       InvokeCallback(std::string());
     81       return;
     82     }
     83 
     84     // The ephemeral app may have been updated and disabled as it requests
     85     // more permissions. In this case we should always prompt before
     86     // launching.
     87     extension_enable_flow_.reset(
     88         new ExtensionEnableFlow(profile(), extension->id(), this));
     89     if (web_contents())
     90       extension_enable_flow_->StartForWebContents(web_contents());
     91     else
     92       extension_enable_flow_->StartForNativeWindow(parent_window_);
     93 
     94     // Keep this object alive until the enable flow is complete.
     95     AddRef();  // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
     96     return;
     97   }
     98 
     99   // Fetch the app from the webstore.
    100   BeginInstall();
    101 }
    102 
    103 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
    104                                            Profile* profile,
    105                                            gfx::NativeWindow parent_window,
    106                                            const Callback& callback)
    107     : WebstoreStandaloneInstaller(webstore_item_id, profile, callback),
    108       parent_window_(parent_window),
    109       dummy_web_contents_(
    110           WebContents::Create(WebContents::CreateParams(profile))) {
    111 }
    112 
    113 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
    114                                            content::WebContents* web_contents,
    115                                            const Callback& callback)
    116     : WebstoreStandaloneInstaller(webstore_item_id,
    117                                   ProfileForWebContents(web_contents),
    118                                   callback),
    119       content::WebContentsObserver(web_contents),
    120       parent_window_(NativeWindowForWebContents(web_contents)) {
    121 }
    122 
    123 EphemeralAppLauncher::~EphemeralAppLauncher() {}
    124 
    125 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
    126   DCHECK(extension);
    127   if (!extension->is_app()) {
    128     LOG(ERROR) << "Unable to launch extension " << extension->id()
    129                << ". It is not an app.";
    130     return;
    131   }
    132 
    133   AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
    134   params.desktop_type =
    135       chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
    136   OpenApplication(params);
    137 }
    138 
    139 bool EphemeralAppLauncher::CheckRequestorAlive() const {
    140   return dummy_web_contents_.get() != NULL || web_contents() != NULL;
    141 }
    142 
    143 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
    144   return GURL::EmptyGURL();
    145 }
    146 
    147 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
    148   return false;
    149 }
    150 
    151 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
    152   return false;
    153 }
    154 
    155 WebContents* EphemeralAppLauncher::GetWebContents() const {
    156   return web_contents() ? web_contents() : dummy_web_contents_.get();
    157 }
    158 
    159 scoped_refptr<ExtensionInstallPrompt::Prompt>
    160 EphemeralAppLauncher::CreateInstallPrompt() const {
    161   DCHECK(extension_.get() != NULL);
    162 
    163   // Skip the prompt by returning null if the app does not need to display
    164   // permission warnings.
    165   extensions::PermissionMessages permissions =
    166       extension_->permissions_data()->GetPermissionMessages();
    167   if (permissions.empty())
    168     return NULL;
    169 
    170   return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
    171       ExtensionInstallPrompt::LAUNCH_PROMPT));
    172 }
    173 
    174 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
    175     const base::DictionaryValue& webstore_data,
    176     std::string* error) const {
    177   *error = "";
    178   return true;
    179 }
    180 
    181 bool EphemeralAppLauncher::CheckRequestorPermitted(
    182     const base::DictionaryValue& webstore_data,
    183     std::string* error) const {
    184   *error = "";
    185   return true;
    186 }
    187 
    188 bool EphemeralAppLauncher::CheckInstallValid(
    189     const base::DictionaryValue& manifest,
    190     std::string* error) {
    191   extension_ = Extension::Create(
    192       base::FilePath(),
    193       extensions::Manifest::INTERNAL,
    194       manifest,
    195       Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
    196       id(),
    197       error);
    198   if (!extension_.get()) {
    199     *error = kInvalidManifestError;
    200     return false;
    201   }
    202 
    203   if (!extension_->is_app()) {
    204     *error = kExtensionTypeError;
    205     return false;
    206   }
    207 
    208   return true;
    209 }
    210 
    211 scoped_ptr<ExtensionInstallPrompt>
    212 EphemeralAppLauncher::CreateInstallUI() {
    213   if (web_contents())
    214     return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
    215 
    216   return make_scoped_ptr(
    217       new ExtensionInstallPrompt(profile(), parent_window_, NULL));
    218 }
    219 
    220 scoped_ptr<WebstoreInstaller::Approval>
    221 EphemeralAppLauncher::CreateApproval() const {
    222   scoped_ptr<WebstoreInstaller::Approval> approval =
    223       WebstoreStandaloneInstaller::CreateApproval();
    224   approval->is_ephemeral = true;
    225   return approval.Pass();
    226 }
    227 
    228 void EphemeralAppLauncher::CompleteInstall(const std::string& error) {
    229   if (error.empty()) {
    230     const Extension* extension =
    231         ExtensionRegistry::Get(profile())
    232             ->GetExtensionById(id(), ExtensionRegistry::ENABLED);
    233     if (extension)
    234       LaunchApp(extension);
    235   }
    236 
    237   WebstoreStandaloneInstaller::CompleteInstall(error);
    238 }
    239 
    240 void EphemeralAppLauncher::WebContentsDestroyed() {
    241   AbortInstall();
    242 }
    243 
    244 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
    245   CompleteInstall(std::string());
    246 }
    247 
    248 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
    249   CompleteInstall(kLaunchAbortedError);
    250 }
    251