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.
5 #include "chrome/browser/apps/ephemeral_app_launcher.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/extensions/extension_install_prompt.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_system.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/extensions/application_launch.h"
14 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_view.h"
20 #include "extensions/common/permissions/permissions_data.h"
22 using content::WebContents;
23 using extensions::Extension;
24 using extensions::ExtensionSystem;
25 using extensions::WebstoreInstaller;
29 const char kInvalidManifestError[] = "Invalid manifest";
30 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted";
31 const char kLaunchAbortedError[] = "Launch aborted";
33 Profile* ProfileForWebContents(content::WebContents* contents) {
37 return Profile::FromBrowserContext(contents->GetBrowserContext());
40 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
44 return contents->GetView()->GetTopLevelNativeWindow();
50 scoped_refptr<EphemeralAppLauncher>
51 EphemeralAppLauncher::CreateForLauncher(
52 const std::string& webstore_item_id,
54 gfx::NativeWindow parent_window,
55 const Callback& callback) {
56 scoped_refptr<EphemeralAppLauncher> installer =
57 new EphemeralAppLauncher(webstore_item_id,
61 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
66 scoped_refptr<EphemeralAppLauncher>
67 EphemeralAppLauncher::CreateForLink(
68 const std::string& webstore_item_id,
69 content::WebContents* web_contents) {
70 scoped_refptr<EphemeralAppLauncher> installer =
71 new EphemeralAppLauncher(webstore_item_id,
74 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
78 void EphemeralAppLauncher::Start() {
79 ExtensionService* extension_service =
80 extensions::ExtensionSystem::Get(profile())->extension_service();
81 DCHECK(extension_service);
83 const Extension* extension = extension_service->GetInstalledExtension(id());
85 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
88 InvokeCallback(std::string());
92 // The ephemeral app may have been updated and disabled as it requests
93 // more permissions. In this case we should always prompt before
95 extension_enable_flow_.reset(
96 new ExtensionEnableFlow(profile(), extension->id(), this));
98 extension_enable_flow_->StartForWebContents(web_contents());
100 extension_enable_flow_->StartForNativeWindow(parent_window_);
102 // Keep this object alive until the enable flow is complete.
103 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
107 // Fetch the app from the webstore.
112 EphemeralAppLauncher::EphemeralAppLauncher(
113 const std::string& webstore_item_id,
115 gfx::NativeWindow parent_window,
116 const Callback& callback)
117 : WebstoreStandaloneInstaller(
121 parent_window_(parent_window),
123 WebContents::Create(WebContents::CreateParams(profile))) {
126 EphemeralAppLauncher::EphemeralAppLauncher(
127 const std::string& webstore_item_id,
128 content::WebContents* web_contents,
129 const Callback& callback)
130 : WebstoreStandaloneInstaller(
132 ProfileForWebContents(web_contents),
134 content::WebContentsObserver(web_contents),
135 parent_window_(NativeWindowForWebContents(web_contents)) {
138 EphemeralAppLauncher::~EphemeralAppLauncher() {}
140 void EphemeralAppLauncher::StartObserving() {
141 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
142 content::Source<Profile>(profile()->GetOriginalProfile()));
145 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
147 if (!extension->is_app()) {
148 LOG(ERROR) << "Unable to launch extension " << extension->id()
149 << ". It is not an app.";
153 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
154 params.desktop_type =
155 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
156 OpenApplication(params);
159 bool EphemeralAppLauncher::CheckRequestorAlive() const {
160 return dummy_web_contents_.get() != NULL || web_contents() != NULL;
163 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
164 return GURL::EmptyGURL();
167 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
171 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
175 WebContents* EphemeralAppLauncher::GetWebContents() const {
176 return web_contents() ? web_contents() : dummy_web_contents_.get();
179 scoped_ptr<ExtensionInstallPrompt::Prompt>
180 EphemeralAppLauncher::CreateInstallPrompt() const {
181 DCHECK(extension_.get() != NULL);
183 // Skip the prompt by returning null if the app does not need to display
184 // permission warnings.
185 extensions::PermissionMessages permissions =
186 extensions::PermissionsData::GetPermissionMessages(extension_.get());
187 if (permissions.empty())
188 return scoped_ptr<ExtensionInstallPrompt::Prompt>();
190 return make_scoped_ptr(new ExtensionInstallPrompt::Prompt(
191 ExtensionInstallPrompt::LAUNCH_PROMPT));
194 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
195 const base::DictionaryValue& webstore_data,
196 std::string* error) const {
201 bool EphemeralAppLauncher::CheckRequestorPermitted(
202 const base::DictionaryValue& webstore_data,
203 std::string* error) const {
208 bool EphemeralAppLauncher::CheckInstallValid(
209 const base::DictionaryValue& manifest,
210 std::string* error) {
211 extension_ = Extension::Create(
213 extensions::Manifest::INTERNAL,
215 Extension::REQUIRE_KEY |
216 Extension::FROM_WEBSTORE |
217 Extension::IS_EPHEMERAL,
220 if (!extension_.get()) {
221 *error = kInvalidManifestError;
225 if (!extension_->is_app()) {
226 *error = kExtensionTypeError;
233 scoped_ptr<ExtensionInstallPrompt>
234 EphemeralAppLauncher::CreateInstallUI() {
236 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
238 return make_scoped_ptr(
239 new ExtensionInstallPrompt(profile(), parent_window_, NULL));
242 scoped_ptr<WebstoreInstaller::Approval>
243 EphemeralAppLauncher::CreateApproval() const {
244 scoped_ptr<WebstoreInstaller::Approval> approval =
245 WebstoreStandaloneInstaller::CreateApproval();
246 approval->is_ephemeral = true;
247 return approval.Pass();
250 void EphemeralAppLauncher::CompleteInstall(const std::string& error) {
252 WebstoreStandaloneInstaller::CompleteInstall(error);
254 // If the installation succeeds, we reach this point as a result of
255 // chrome::NOTIFICATION_EXTENSION_INSTALLED, but this is broadcasted before
256 // ExtensionService has added the extension to its list of installed
257 // extensions and is too early to launch the app. Instead, we will launch at
258 // chrome::NOTIFICATION_EXTENSION_LOADED.
259 // TODO(tmdiep): Refactor extensions/WebstoreInstaller or
260 // WebstoreStandaloneInstaller to support this cleanly.
263 void EphemeralAppLauncher::WebContentsDestroyed(
264 content::WebContents* web_contents) {
268 void EphemeralAppLauncher::Observe(
270 const content::NotificationSource& source,
271 const content::NotificationDetails& details) {
273 case chrome::NOTIFICATION_EXTENSION_LOADED: {
274 const extensions::Extension* extension =
275 content::Details<const extensions::Extension>(details).ptr();
277 if (extension->id() == id()) {
278 LaunchApp(extension);
279 WebstoreStandaloneInstaller::CompleteInstall(std::string());
289 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
290 ExtensionService* extension_service =
291 extensions::ExtensionSystem::Get(profile())->extension_service();
292 DCHECK(extension_service);
294 const Extension* extension = extension_service->GetExtensionById(id(), false);
296 LaunchApp(extension);
297 WebstoreStandaloneInstaller::CompleteInstall(std::string());
299 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);
303 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
304 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);