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/extensions/extension_install_prompt.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/extensions/extension_util.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/extensions/application_launch.h"
12 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
13 #include "content/public/browser/web_contents.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/browser/extension_system.h"
16 #include "extensions/common/permissions/permissions_data.h"
18 using content::WebContents
;
19 using extensions::Extension
;
20 using extensions::ExtensionSystem
;
21 using extensions::WebstoreInstaller
;
25 const char kInvalidManifestError
[] = "Invalid manifest";
26 const char kExtensionTypeError
[] = "Ephemeral extensions are not permitted";
27 const char kLaunchAbortedError
[] = "Launch aborted";
29 Profile
* ProfileForWebContents(content::WebContents
* contents
) {
33 return Profile::FromBrowserContext(contents
->GetBrowserContext());
36 gfx::NativeWindow
NativeWindowForWebContents(content::WebContents
* contents
) {
40 return contents
->GetTopLevelNativeWindow();
46 scoped_refptr
<EphemeralAppLauncher
>
47 EphemeralAppLauncher::CreateForLauncher(
48 const std::string
& webstore_item_id
,
50 gfx::NativeWindow parent_window
,
51 const Callback
& callback
) {
52 scoped_refptr
<EphemeralAppLauncher
> installer
=
53 new EphemeralAppLauncher(webstore_item_id
,
57 installer
->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER
);
62 scoped_refptr
<EphemeralAppLauncher
>
63 EphemeralAppLauncher::CreateForLink(
64 const std::string
& webstore_item_id
,
65 content::WebContents
* web_contents
) {
66 scoped_refptr
<EphemeralAppLauncher
> installer
=
67 new EphemeralAppLauncher(webstore_item_id
,
70 installer
->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER
);
74 void EphemeralAppLauncher::Start() {
75 ExtensionService
* extension_service
=
76 extensions::ExtensionSystem::Get(profile())->extension_service();
77 DCHECK(extension_service
);
79 const Extension
* extension
= extension_service
->GetInstalledExtension(id());
81 if (extensions::util::IsAppLaunchableWithoutEnabling(extension
->id(),
84 InvokeCallback(std::string());
88 // The ephemeral app may have been updated and disabled as it requests
89 // more permissions. In this case we should always prompt before
91 extension_enable_flow_
.reset(
92 new ExtensionEnableFlow(profile(), extension
->id(), this));
94 extension_enable_flow_
->StartForWebContents(web_contents());
96 extension_enable_flow_
->StartForNativeWindow(parent_window_
);
98 // Keep this object alive until the enable flow is complete.
99 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
103 // Fetch the app from the webstore.
108 EphemeralAppLauncher::EphemeralAppLauncher(const std::string
& webstore_item_id
,
110 gfx::NativeWindow parent_window
,
111 const Callback
& callback
)
112 : WebstoreStandaloneInstaller(webstore_item_id
, profile
, callback
),
113 extension_registry_observer_(this),
114 parent_window_(parent_window
),
116 WebContents::Create(WebContents::CreateParams(profile
))) {
119 EphemeralAppLauncher::EphemeralAppLauncher(const std::string
& webstore_item_id
,
120 content::WebContents
* web_contents
,
121 const Callback
& callback
)
122 : WebstoreStandaloneInstaller(webstore_item_id
,
123 ProfileForWebContents(web_contents
),
125 content::WebContentsObserver(web_contents
),
126 extension_registry_observer_(this),
127 parent_window_(NativeWindowForWebContents(web_contents
)) {
130 EphemeralAppLauncher::~EphemeralAppLauncher() {}
132 void EphemeralAppLauncher::StartObserving() {
133 extension_registry_observer_
.Add(
134 extensions::ExtensionRegistry::Get(profile()));
137 void EphemeralAppLauncher::LaunchApp(const Extension
* extension
) const {
139 if (!extension
->is_app()) {
140 LOG(ERROR
) << "Unable to launch extension " << extension
->id()
141 << ". It is not an app.";
145 AppLaunchParams
params(profile(), extension
, NEW_FOREGROUND_TAB
);
146 params
.desktop_type
=
147 chrome::GetHostDesktopTypeForNativeWindow(parent_window_
);
148 OpenApplication(params
);
151 bool EphemeralAppLauncher::CheckRequestorAlive() const {
152 return dummy_web_contents_
.get() != NULL
|| web_contents() != NULL
;
155 const GURL
& EphemeralAppLauncher::GetRequestorURL() const {
156 return GURL::EmptyGURL();
159 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
163 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
167 WebContents
* EphemeralAppLauncher::GetWebContents() const {
168 return web_contents() ? web_contents() : dummy_web_contents_
.get();
171 scoped_ptr
<ExtensionInstallPrompt::Prompt
>
172 EphemeralAppLauncher::CreateInstallPrompt() const {
173 DCHECK(extension_
.get() != NULL
);
175 // Skip the prompt by returning null if the app does not need to display
176 // permission warnings.
177 extensions::PermissionMessages permissions
=
178 extensions::PermissionsData::GetPermissionMessages(extension_
.get());
179 if (permissions
.empty())
180 return scoped_ptr
<ExtensionInstallPrompt::Prompt
>();
182 return make_scoped_ptr(new ExtensionInstallPrompt::Prompt(
183 ExtensionInstallPrompt::LAUNCH_PROMPT
));
186 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
187 const base::DictionaryValue
& webstore_data
,
188 std::string
* error
) const {
193 bool EphemeralAppLauncher::CheckRequestorPermitted(
194 const base::DictionaryValue
& webstore_data
,
195 std::string
* error
) const {
200 bool EphemeralAppLauncher::CheckInstallValid(
201 const base::DictionaryValue
& manifest
,
202 std::string
* error
) {
203 extension_
= Extension::Create(
205 extensions::Manifest::INTERNAL
,
207 Extension::REQUIRE_KEY
|
208 Extension::FROM_WEBSTORE
|
209 Extension::IS_EPHEMERAL
,
212 if (!extension_
.get()) {
213 *error
= kInvalidManifestError
;
217 if (!extension_
->is_app()) {
218 *error
= kExtensionTypeError
;
225 scoped_ptr
<ExtensionInstallPrompt
>
226 EphemeralAppLauncher::CreateInstallUI() {
228 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
230 return make_scoped_ptr(
231 new ExtensionInstallPrompt(profile(), parent_window_
, NULL
));
234 scoped_ptr
<WebstoreInstaller::Approval
>
235 EphemeralAppLauncher::CreateApproval() const {
236 scoped_ptr
<WebstoreInstaller::Approval
> approval
=
237 WebstoreStandaloneInstaller::CreateApproval();
238 approval
->is_ephemeral
= true;
239 return approval
.Pass();
242 void EphemeralAppLauncher::CompleteInstall(const std::string
& error
) {
244 WebstoreStandaloneInstaller::CompleteInstall(error
);
246 // If the installation succeeds, we reach this point as a result of
247 // chrome::NOTIFICATION_EXTENSION_INSTALLED, but this is broadcasted before
248 // ExtensionService has added the extension to its list of installed
249 // extensions and is too early to launch the app. Instead, we will launch at
250 // EphemeralAppLauncher::OnExtensionLoaded().
251 // TODO(tmdiep): Refactor extensions/WebstoreInstaller or
252 // WebstoreStandaloneInstaller to support this cleanly.
255 void EphemeralAppLauncher::WebContentsDestroyed() {
259 void EphemeralAppLauncher::OnExtensionLoaded(
260 content::BrowserContext
* browser_context
,
261 const Extension
* extension
) {
262 if (extension
->id() == id()) {
263 LaunchApp(extension
);
264 WebstoreStandaloneInstaller::CompleteInstall(std::string());
268 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
269 ExtensionService
* extension_service
=
270 extensions::ExtensionSystem::Get(profile())->extension_service();
271 DCHECK(extension_service
);
273 const Extension
* extension
= extension_service
->GetExtensionById(id(), false);
275 LaunchApp(extension
);
276 WebstoreStandaloneInstaller::CompleteInstall(std::string());
278 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError
);
282 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated
) {
283 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError
);