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 "content/public/browser/web_contents_view.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/common/permissions/permissions_data.h"
19 using content::WebContents
;
20 using extensions::Extension
;
21 using extensions::ExtensionSystem
;
22 using extensions::WebstoreInstaller
;
26 const char kInvalidManifestError
[] = "Invalid manifest";
27 const char kExtensionTypeError
[] = "Ephemeral extensions are not permitted";
28 const char kLaunchAbortedError
[] = "Launch aborted";
30 Profile
* ProfileForWebContents(content::WebContents
* contents
) {
34 return Profile::FromBrowserContext(contents
->GetBrowserContext());
37 gfx::NativeWindow
NativeWindowForWebContents(content::WebContents
* contents
) {
41 return contents
->GetView()->GetTopLevelNativeWindow();
47 scoped_refptr
<EphemeralAppLauncher
>
48 EphemeralAppLauncher::CreateForLauncher(
49 const std::string
& webstore_item_id
,
51 gfx::NativeWindow parent_window
,
52 const Callback
& callback
) {
53 scoped_refptr
<EphemeralAppLauncher
> installer
=
54 new EphemeralAppLauncher(webstore_item_id
,
58 installer
->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER
);
63 scoped_refptr
<EphemeralAppLauncher
>
64 EphemeralAppLauncher::CreateForLink(
65 const std::string
& webstore_item_id
,
66 content::WebContents
* web_contents
) {
67 scoped_refptr
<EphemeralAppLauncher
> installer
=
68 new EphemeralAppLauncher(webstore_item_id
,
71 installer
->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER
);
75 void EphemeralAppLauncher::Start() {
76 ExtensionService
* extension_service
=
77 extensions::ExtensionSystem::Get(profile())->extension_service();
78 DCHECK(extension_service
);
80 const Extension
* extension
= extension_service
->GetInstalledExtension(id());
82 if (extensions::util::IsAppLaunchableWithoutEnabling(extension
->id(),
85 InvokeCallback(std::string());
89 // The ephemeral app may have been updated and disabled as it requests
90 // more permissions. In this case we should always prompt before
92 extension_enable_flow_
.reset(
93 new ExtensionEnableFlow(profile(), extension
->id(), this));
95 extension_enable_flow_
->StartForWebContents(web_contents());
97 extension_enable_flow_
->StartForNativeWindow(parent_window_
);
99 // Keep this object alive until the enable flow is complete.
100 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
104 // Fetch the app from the webstore.
109 EphemeralAppLauncher::EphemeralAppLauncher(const std::string
& webstore_item_id
,
111 gfx::NativeWindow parent_window
,
112 const Callback
& callback
)
113 : WebstoreStandaloneInstaller(webstore_item_id
, profile
, callback
),
114 extension_registry_observer_(this),
115 parent_window_(parent_window
),
117 WebContents::Create(WebContents::CreateParams(profile
))) {
120 EphemeralAppLauncher::EphemeralAppLauncher(const std::string
& webstore_item_id
,
121 content::WebContents
* web_contents
,
122 const Callback
& callback
)
123 : WebstoreStandaloneInstaller(webstore_item_id
,
124 ProfileForWebContents(web_contents
),
126 content::WebContentsObserver(web_contents
),
127 extension_registry_observer_(this),
128 parent_window_(NativeWindowForWebContents(web_contents
)) {
131 EphemeralAppLauncher::~EphemeralAppLauncher() {}
133 void EphemeralAppLauncher::StartObserving() {
134 extension_registry_observer_
.Add(
135 extensions::ExtensionRegistry::Get(profile()));
138 void EphemeralAppLauncher::LaunchApp(const Extension
* extension
) const {
140 if (!extension
->is_app()) {
141 LOG(ERROR
) << "Unable to launch extension " << extension
->id()
142 << ". It is not an app.";
146 AppLaunchParams
params(profile(), extension
, NEW_FOREGROUND_TAB
);
147 params
.desktop_type
=
148 chrome::GetHostDesktopTypeForNativeWindow(parent_window_
);
149 OpenApplication(params
);
152 bool EphemeralAppLauncher::CheckRequestorAlive() const {
153 return dummy_web_contents_
.get() != NULL
|| web_contents() != NULL
;
156 const GURL
& EphemeralAppLauncher::GetRequestorURL() const {
157 return GURL::EmptyGURL();
160 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
164 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
168 WebContents
* EphemeralAppLauncher::GetWebContents() const {
169 return web_contents() ? web_contents() : dummy_web_contents_
.get();
172 scoped_ptr
<ExtensionInstallPrompt::Prompt
>
173 EphemeralAppLauncher::CreateInstallPrompt() const {
174 DCHECK(extension_
.get() != NULL
);
176 // Skip the prompt by returning null if the app does not need to display
177 // permission warnings.
178 extensions::PermissionMessages permissions
=
179 extensions::PermissionsData::GetPermissionMessages(extension_
.get());
180 if (permissions
.empty())
181 return scoped_ptr
<ExtensionInstallPrompt::Prompt
>();
183 return make_scoped_ptr(new ExtensionInstallPrompt::Prompt(
184 ExtensionInstallPrompt::LAUNCH_PROMPT
));
187 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
188 const base::DictionaryValue
& webstore_data
,
189 std::string
* error
) const {
194 bool EphemeralAppLauncher::CheckRequestorPermitted(
195 const base::DictionaryValue
& webstore_data
,
196 std::string
* error
) const {
201 bool EphemeralAppLauncher::CheckInstallValid(
202 const base::DictionaryValue
& manifest
,
203 std::string
* error
) {
204 extension_
= Extension::Create(
206 extensions::Manifest::INTERNAL
,
208 Extension::REQUIRE_KEY
|
209 Extension::FROM_WEBSTORE
|
210 Extension::IS_EPHEMERAL
,
213 if (!extension_
.get()) {
214 *error
= kInvalidManifestError
;
218 if (!extension_
->is_app()) {
219 *error
= kExtensionTypeError
;
226 scoped_ptr
<ExtensionInstallPrompt
>
227 EphemeralAppLauncher::CreateInstallUI() {
229 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
231 return make_scoped_ptr(
232 new ExtensionInstallPrompt(profile(), parent_window_
, NULL
));
235 scoped_ptr
<WebstoreInstaller::Approval
>
236 EphemeralAppLauncher::CreateApproval() const {
237 scoped_ptr
<WebstoreInstaller::Approval
> approval
=
238 WebstoreStandaloneInstaller::CreateApproval();
239 approval
->is_ephemeral
= true;
240 return approval
.Pass();
243 void EphemeralAppLauncher::CompleteInstall(const std::string
& error
) {
245 WebstoreStandaloneInstaller::CompleteInstall(error
);
247 // If the installation succeeds, we reach this point as a result of
248 // chrome::NOTIFICATION_EXTENSION_INSTALLED, but this is broadcasted before
249 // ExtensionService has added the extension to its list of installed
250 // extensions and is too early to launch the app. Instead, we will launch at
251 // EphemeralAppLauncher::OnExtensionLoaded().
252 // TODO(tmdiep): Refactor extensions/WebstoreInstaller or
253 // WebstoreStandaloneInstaller to support this cleanly.
256 void EphemeralAppLauncher::WebContentsDestroyed(
257 content::WebContents
* web_contents
) {
261 void EphemeralAppLauncher::OnExtensionLoaded(
262 content::BrowserContext
* browser_context
,
263 const Extension
* extension
) {
264 if (extension
->id() == id()) {
265 LaunchApp(extension
);
266 WebstoreStandaloneInstaller::CompleteInstall(std::string());
270 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
271 ExtensionService
* extension_service
=
272 extensions::ExtensionSystem::Get(profile())->extension_service();
273 DCHECK(extension_service
);
275 const Extension
* extension
= extension_service
->GetExtensionById(id(), false);
277 LaunchApp(extension
);
278 WebstoreStandaloneInstaller::CompleteInstall(std::string());
280 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError
);
284 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated
) {
285 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError
);