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_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/extensions/application_launch.h"
13 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
14 #include "content/public/browser/notification_details.h"
15 #include "content/public/browser/notification_source.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_view.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/common/permissions/permissions_data.h"
21 using content::WebContents;
22 using extensions::Extension;
23 using extensions::ExtensionSystem;
24 using extensions::WebstoreInstaller;
28 const char kInvalidManifestError[] = "Invalid manifest";
29 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted";
30 const char kLaunchAbortedError[] = "Launch aborted";
32 Profile* ProfileForWebContents(content::WebContents* contents) {
36 return Profile::FromBrowserContext(contents->GetBrowserContext());
39 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
43 return contents->GetView()->GetTopLevelNativeWindow();
49 scoped_refptr<EphemeralAppLauncher>
50 EphemeralAppLauncher::CreateForLauncher(
51 const std::string& webstore_item_id,
53 gfx::NativeWindow parent_window,
54 const Callback& callback) {
55 scoped_refptr<EphemeralAppLauncher> installer =
56 new EphemeralAppLauncher(webstore_item_id,
60 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
65 scoped_refptr<EphemeralAppLauncher>
66 EphemeralAppLauncher::CreateForLink(
67 const std::string& webstore_item_id,
68 content::WebContents* web_contents) {
69 scoped_refptr<EphemeralAppLauncher> installer =
70 new EphemeralAppLauncher(webstore_item_id,
73 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
77 void EphemeralAppLauncher::Start() {
78 ExtensionService* extension_service =
79 extensions::ExtensionSystem::Get(profile())->extension_service();
80 DCHECK(extension_service);
82 const Extension* extension = extension_service->GetInstalledExtension(id());
84 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
87 InvokeCallback(std::string());
91 // The ephemeral app may have been updated and disabled as it requests
92 // more permissions. In this case we should always prompt before
94 extension_enable_flow_.reset(
95 new ExtensionEnableFlow(profile(), extension->id(), this));
97 extension_enable_flow_->StartForWebContents(web_contents());
99 extension_enable_flow_->StartForNativeWindow(parent_window_);
101 // Keep this object alive until the enable flow is complete.
102 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
106 // Fetch the app from the webstore.
111 EphemeralAppLauncher::EphemeralAppLauncher(
112 const std::string& webstore_item_id,
114 gfx::NativeWindow parent_window,
115 const Callback& callback)
116 : WebstoreStandaloneInstaller(
120 parent_window_(parent_window),
122 WebContents::Create(WebContents::CreateParams(profile))) {
125 EphemeralAppLauncher::EphemeralAppLauncher(
126 const std::string& webstore_item_id,
127 content::WebContents* web_contents,
128 const Callback& callback)
129 : WebstoreStandaloneInstaller(
131 ProfileForWebContents(web_contents),
133 content::WebContentsObserver(web_contents),
134 parent_window_(NativeWindowForWebContents(web_contents)) {
137 EphemeralAppLauncher::~EphemeralAppLauncher() {}
139 void EphemeralAppLauncher::StartObserving() {
140 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
141 content::Source<Profile>(profile()->GetOriginalProfile()));
144 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
146 if (!extension->is_app()) {
147 LOG(ERROR) << "Unable to launch extension " << extension->id()
148 << ". It is not an app.";
152 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
153 params.desktop_type =
154 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
155 OpenApplication(params);
158 bool EphemeralAppLauncher::CheckRequestorAlive() const {
159 return dummy_web_contents_.get() != NULL || web_contents() != NULL;
162 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
163 return GURL::EmptyGURL();
166 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
170 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
174 WebContents* EphemeralAppLauncher::GetWebContents() const {
175 return web_contents() ? web_contents() : dummy_web_contents_.get();
178 scoped_ptr<ExtensionInstallPrompt::Prompt>
179 EphemeralAppLauncher::CreateInstallPrompt() const {
180 DCHECK(extension_.get() != NULL);
182 // Skip the prompt by returning null if the app does not need to display
183 // permission warnings.
184 extensions::PermissionMessages permissions =
185 extensions::PermissionsData::GetPermissionMessages(extension_.get());
186 if (permissions.empty())
187 return scoped_ptr<ExtensionInstallPrompt::Prompt>();
189 return make_scoped_ptr(new ExtensionInstallPrompt::Prompt(
190 ExtensionInstallPrompt::LAUNCH_PROMPT));
193 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
194 const base::DictionaryValue& webstore_data,
195 std::string* error) const {
200 bool EphemeralAppLauncher::CheckRequestorPermitted(
201 const base::DictionaryValue& webstore_data,
202 std::string* error) const {
207 bool EphemeralAppLauncher::CheckInstallValid(
208 const base::DictionaryValue& manifest,
209 std::string* error) {
210 extension_ = Extension::Create(
212 extensions::Manifest::INTERNAL,
214 Extension::REQUIRE_KEY |
215 Extension::FROM_WEBSTORE |
216 Extension::IS_EPHEMERAL,
219 if (!extension_.get()) {
220 *error = kInvalidManifestError;
224 if (!extension_->is_app()) {
225 *error = kExtensionTypeError;
232 scoped_ptr<ExtensionInstallPrompt>
233 EphemeralAppLauncher::CreateInstallUI() {
235 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
237 return make_scoped_ptr(
238 new ExtensionInstallPrompt(profile(), parent_window_, NULL));
241 scoped_ptr<WebstoreInstaller::Approval>
242 EphemeralAppLauncher::CreateApproval() const {
243 scoped_ptr<WebstoreInstaller::Approval> approval =
244 WebstoreStandaloneInstaller::CreateApproval();
245 approval->is_ephemeral = true;
246 return approval.Pass();
249 void EphemeralAppLauncher::CompleteInstall(const std::string& error) {
251 WebstoreStandaloneInstaller::CompleteInstall(error);
253 // If the installation succeeds, we reach this point as a result of
254 // chrome::NOTIFICATION_EXTENSION_INSTALLED, but this is broadcasted before
255 // ExtensionService has added the extension to its list of installed
256 // extensions and is too early to launch the app. Instead, we will launch at
257 // chrome::NOTIFICATION_EXTENSION_LOADED.
258 // TODO(tmdiep): Refactor extensions/WebstoreInstaller or
259 // WebstoreStandaloneInstaller to support this cleanly.
262 void EphemeralAppLauncher::WebContentsDestroyed(
263 content::WebContents* web_contents) {
267 void EphemeralAppLauncher::Observe(
269 const content::NotificationSource& source,
270 const content::NotificationDetails& details) {
272 case chrome::NOTIFICATION_EXTENSION_LOADED: {
273 const extensions::Extension* extension =
274 content::Details<const extensions::Extension>(details).ptr();
276 if (extension->id() == id()) {
277 LaunchApp(extension);
278 WebstoreStandaloneInstaller::CompleteInstall(std::string());
288 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
289 ExtensionService* extension_service =
290 extensions::ExtensionSystem::Get(profile())->extension_service();
291 DCHECK(extension_service);
293 const Extension* extension = extension_service->GetExtensionById(id(), false);
295 LaunchApp(extension);
296 WebstoreStandaloneInstaller::CompleteInstall(std::string());
298 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);
302 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
303 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);