Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / apps / ephemeral_app_launcher.cc
blob828f4cb804012d1b86b7be2b38a67a9ad6227697
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;
27 namespace {
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) {
34 if (!contents)
35 return NULL;
37 return Profile::FromBrowserContext(contents->GetBrowserContext());
40 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
41 if (!contents)
42 return NULL;
44 return contents->GetView()->GetTopLevelNativeWindow();
47 } // namespace
49 // static
50 scoped_refptr<EphemeralAppLauncher>
51 EphemeralAppLauncher::CreateForLauncher(
52 const std::string& webstore_item_id,
53 Profile* profile,
54 gfx::NativeWindow parent_window,
55 const Callback& callback) {
56 scoped_refptr<EphemeralAppLauncher> installer =
57 new EphemeralAppLauncher(webstore_item_id,
58 profile,
59 parent_window,
60 callback);
61 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
62 return installer;
65 // static
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,
72 web_contents,
73 Callback());
74 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
75 return installer;
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());
84 if (extension) {
85 if (extension_util::IsAppLaunchableWithoutEnabling(extension->id(),
86 extension_service)) {
87 LaunchApp(extension);
88 InvokeCallback(std::string());
89 return;
92 // The ephemeral app may have been updated and disabled as it requests
93 // more permissions. In this case we should always prompt before
94 // launching.
95 extension_enable_flow_.reset(
96 new ExtensionEnableFlow(profile(), extension->id(), this));
97 if (web_contents())
98 extension_enable_flow_->StartForWebContents(web_contents());
99 else
100 extension_enable_flow_->StartForNativeWindow(parent_window_);
102 // Keep this object alive until the enable flow is complete.
103 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
104 return;
107 // Fetch the app from the webstore.
108 StartObserving();
109 BeginInstall();
112 EphemeralAppLauncher::EphemeralAppLauncher(
113 const std::string& webstore_item_id,
114 Profile* profile,
115 gfx::NativeWindow parent_window,
116 const Callback& callback)
117 : WebstoreStandaloneInstaller(
118 webstore_item_id,
119 profile,
120 callback),
121 parent_window_(parent_window),
122 dummy_web_contents_(
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(
131 webstore_item_id,
132 ProfileForWebContents(web_contents),
133 callback),
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 {
146 DCHECK(extension);
147 if (!extension->is_app()) {
148 LOG(ERROR) << "Unable to launch extension " << extension->id()
149 << ". It is not an app.";
150 return;
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 {
168 return false;
171 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
172 return false;
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 {
197 *error = "";
198 return true;
201 bool EphemeralAppLauncher::CheckRequestorPermitted(
202 const base::DictionaryValue& webstore_data,
203 std::string* error) const {
204 *error = "";
205 return true;
208 bool EphemeralAppLauncher::CheckInstallValid(
209 const base::DictionaryValue& manifest,
210 std::string* error) {
211 extension_ = Extension::Create(
212 base::FilePath(),
213 extensions::Manifest::INTERNAL,
214 manifest,
215 Extension::REQUIRE_KEY |
216 Extension::FROM_WEBSTORE |
217 Extension::IS_EPHEMERAL,
218 id(),
219 error);
220 if (!extension_.get()) {
221 *error = kInvalidManifestError;
222 return false;
225 if (!extension_->is_app()) {
226 *error = kExtensionTypeError;
227 return false;
230 return true;
233 scoped_ptr<ExtensionInstallPrompt>
234 EphemeralAppLauncher::CreateInstallUI() {
235 if (web_contents())
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) {
251 if (!error.empty())
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) {
265 AbortInstall();
268 void EphemeralAppLauncher::Observe(
269 int type,
270 const content::NotificationSource& source,
271 const content::NotificationDetails& details) {
272 switch (type) {
273 case chrome::NOTIFICATION_EXTENSION_LOADED: {
274 const extensions::Extension* extension =
275 content::Details<const extensions::Extension>(details).ptr();
276 DCHECK(extension);
277 if (extension->id() == id()) {
278 LaunchApp(extension);
279 WebstoreStandaloneInstaller::CompleteInstall(std::string());
281 break;
284 default:
285 NOTREACHED();
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);
295 if (extension) {
296 LaunchApp(extension);
297 WebstoreStandaloneInstaller::CompleteInstall(std::string());
298 } else {
299 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);
303 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
304 WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);