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 (extension_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
);