Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / ephemeral_app_launcher.cc
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.
4
5 #include "chrome/browser/apps/ephemeral_app_launcher.h"
6
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"
21
22 using content::WebContents;
23 using extensions::Extension;
24 using extensions::ExtensionSystem;
25 using extensions::WebstoreInstaller;
26
27 namespace {
28
29 const char kInvalidManifestError[] = "Invalid manifest";
30 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted";
31 const char kLaunchAbortedError[] = "Launch aborted";
32
33 Profile* ProfileForWebContents(content::WebContents* contents) {
34   if (!contents)
35     return NULL;
36
37   return Profile::FromBrowserContext(contents->GetBrowserContext());
38 }
39
40 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
41   if (!contents)
42     return NULL;
43
44   return contents->GetView()->GetTopLevelNativeWindow();
45 }
46
47 }  // namespace
48
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;
63 }
64
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;
76 }
77
78 void EphemeralAppLauncher::Start() {
79   ExtensionService* extension_service =
80       extensions::ExtensionSystem::Get(profile())->extension_service();
81   DCHECK(extension_service);
82
83   const Extension* extension = extension_service->GetInstalledExtension(id());
84   if (extension) {
85     if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
86                                                          profile())) {
87       LaunchApp(extension);
88       InvokeCallback(std::string());
89       return;
90     }
91
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_);
101
102     // Keep this object alive until the enable flow is complete.
103     AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
104     return;
105   }
106
107   // Fetch the app from the webstore.
108   StartObserving();
109   BeginInstall();
110 }
111
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))) {
124 }
125
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)) {
136 }
137
138 EphemeralAppLauncher::~EphemeralAppLauncher() {}
139
140 void EphemeralAppLauncher::StartObserving() {
141   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
142                  content::Source<Profile>(profile()->GetOriginalProfile()));
143 }
144
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;
151   }
152
153   AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
154   params.desktop_type =
155       chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
156   OpenApplication(params);
157 }
158
159 bool EphemeralAppLauncher::CheckRequestorAlive() const {
160   return dummy_web_contents_.get() != NULL || web_contents() != NULL;
161 }
162
163 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
164   return GURL::EmptyGURL();
165 }
166
167 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
168   return false;
169 }
170
171 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
172   return false;
173 }
174
175 WebContents* EphemeralAppLauncher::GetWebContents() const {
176   return web_contents() ? web_contents() : dummy_web_contents_.get();
177 }
178
179 scoped_ptr<ExtensionInstallPrompt::Prompt>
180 EphemeralAppLauncher::CreateInstallPrompt() const {
181   DCHECK(extension_.get() != NULL);
182
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>();
189
190   return make_scoped_ptr(new ExtensionInstallPrompt::Prompt(
191       ExtensionInstallPrompt::LAUNCH_PROMPT));
192 }
193
194 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
195     const base::DictionaryValue& webstore_data,
196     std::string* error) const {
197   *error = "";
198   return true;
199 }
200
201 bool EphemeralAppLauncher::CheckRequestorPermitted(
202     const base::DictionaryValue& webstore_data,
203     std::string* error) const {
204   *error = "";
205   return true;
206 }
207
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;
223   }
224
225   if (!extension_->is_app()) {
226     *error = kExtensionTypeError;
227     return false;
228   }
229
230   return true;
231 }
232
233 scoped_ptr<ExtensionInstallPrompt>
234 EphemeralAppLauncher::CreateInstallUI() {
235   if (web_contents())
236     return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
237
238   return make_scoped_ptr(
239       new ExtensionInstallPrompt(profile(), parent_window_, NULL));
240 }
241
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();
248 }
249
250 void EphemeralAppLauncher::CompleteInstall(const std::string& error) {
251   if (!error.empty())
252     WebstoreStandaloneInstaller::CompleteInstall(error);
253
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.
261 }
262
263 void EphemeralAppLauncher::WebContentsDestroyed(
264     content::WebContents* web_contents) {
265   AbortInstall();
266 }
267
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());
280       }
281       break;
282     }
283
284     default:
285       NOTREACHED();
286   }
287 }
288
289 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
290   ExtensionService* extension_service =
291       extensions::ExtensionSystem::Get(profile())->extension_service();
292   DCHECK(extension_service);
293
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);
300   }
301 }
302
303 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
304   WebstoreStandaloneInstaller::CompleteInstall(kLaunchAbortedError);
305 }