• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProfileForWebContents(content::WebContents * contents)27 Profile* ProfileForWebContents(content::WebContents* contents) {
28   if (!contents)
29     return NULL;
30 
31   return Profile::FromBrowserContext(contents->GetBrowserContext());
32 }
33 
NativeWindowForWebContents(content::WebContents * contents)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>
CreateForLauncher(const std::string & webstore_item_id,Profile * profile,gfx::NativeWindow parent_window,const Callback & callback)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>
CreateForLink(const std::string & webstore_item_id,content::WebContents * web_contents)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 
Start()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 
EphemeralAppLauncher(const std::string & webstore_item_id,Profile * profile,gfx::NativeWindow parent_window,const Callback & callback)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 
EphemeralAppLauncher(const std::string & webstore_item_id,content::WebContents * web_contents,const Callback & callback)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 
~EphemeralAppLauncher()123 EphemeralAppLauncher::~EphemeralAppLauncher() {}
124 
LaunchApp(const Extension * extension) const125 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 
CheckRequestorAlive() const139 bool EphemeralAppLauncher::CheckRequestorAlive() const {
140   return dummy_web_contents_.get() != NULL || web_contents() != NULL;
141 }
142 
GetRequestorURL() const143 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
144   return GURL::EmptyGURL();
145 }
146 
ShouldShowPostInstallUI() const147 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
148   return false;
149 }
150 
ShouldShowAppInstalledBubble() const151 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
152   return false;
153 }
154 
GetWebContents() const155 WebContents* EphemeralAppLauncher::GetWebContents() const {
156   return web_contents() ? web_contents() : dummy_web_contents_.get();
157 }
158 
159 scoped_refptr<ExtensionInstallPrompt::Prompt>
CreateInstallPrompt() const160 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 
CheckInlineInstallPermitted(const base::DictionaryValue & webstore_data,std::string * error) const174 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
175     const base::DictionaryValue& webstore_data,
176     std::string* error) const {
177   *error = "";
178   return true;
179 }
180 
CheckRequestorPermitted(const base::DictionaryValue & webstore_data,std::string * error) const181 bool EphemeralAppLauncher::CheckRequestorPermitted(
182     const base::DictionaryValue& webstore_data,
183     std::string* error) const {
184   *error = "";
185   return true;
186 }
187 
CheckInstallValid(const base::DictionaryValue & manifest,std::string * error)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>
CreateInstallUI()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>
CreateApproval() const221 EphemeralAppLauncher::CreateApproval() const {
222   scoped_ptr<WebstoreInstaller::Approval> approval =
223       WebstoreStandaloneInstaller::CreateApproval();
224   approval->is_ephemeral = true;
225   return approval.Pass();
226 }
227 
CompleteInstall(const std::string & error)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 
WebContentsDestroyed()240 void EphemeralAppLauncher::WebContentsDestroyed() {
241   AbortInstall();
242 }
243 
ExtensionEnableFlowFinished()244 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
245   CompleteInstall(std::string());
246 }
247 
ExtensionEnableFlowAborted(bool user_initiated)248 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
249   CompleteInstall(kLaunchAbortedError);
250 }
251