1 // Copyright (c) 2012 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/ui/extensions/application_launch.h"
9 #include "apps/launcher.h"
10 #include "base/metrics/histogram.h"
11 #include "chrome/browser/apps/per_app_settings_service.h"
12 #include "chrome/browser/apps/per_app_settings_service_factory.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/tab_helper.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/app_list/app_list_service.h"
17 #include "chrome/browser/ui/extensions/app_launch_params.h"
18 #include "chrome/browser/ui/extensions/application_launch_web_app.h"
19 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
20 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
21 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
22 #include "chrome/common/url_constants.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/features/feature.h"
29 #include "extensions/common/features/feature_provider.h"
30 #include "extensions/common/manifest_handlers/options_page_info.h"
32 using content::WebContents;
33 using extensions::Extension;
34 using extensions::ExtensionPrefs;
35 using extensions::ExtensionRegistry;
39 #if !defined(USE_ATHENA)
40 // Shows the app list for |desktop_type| and returns the app list's window.
41 gfx::NativeWindow ShowAppListAndGetNativeWindow(
42 chrome::HostDesktopType desktop_type) {
43 AppListService* app_list_service = AppListService::Get(desktop_type);
44 app_list_service->Show();
45 return app_list_service->GetAppListWindow();
49 // Attempts to launch an app, prompting the user to enable it if necessary. If
50 // a prompt is required it will be shown inside the window returned by
51 // |parent_window_getter|.
52 // This class manages its own lifetime.
53 class EnableViaDialogFlow : public ExtensionEnableFlowDelegate {
56 ExtensionService* service,
58 const std::string& extension_id,
59 const base::Callback<gfx::NativeWindow(void)>& parent_window_getter,
60 const base::Closure& callback)
63 extension_id_(extension_id),
64 parent_window_getter_(parent_window_getter),
68 ~EnableViaDialogFlow() override {}
71 DCHECK(!service_->IsExtensionEnabled(extension_id_));
72 flow_.reset(new ExtensionEnableFlow(profile_, extension_id_, this));
73 flow_->StartForCurrentlyNonexistentWindow(parent_window_getter_);
77 // ExtensionEnableFlowDelegate overrides.
78 void ExtensionEnableFlowFinished() override {
79 const Extension* extension =
80 service_->GetExtensionById(extension_id_, false);
87 void ExtensionEnableFlowAborted(bool user_initiated) override { delete this; }
89 ExtensionService* service_;
91 std::string extension_id_;
92 base::Callback<gfx::NativeWindow(void)> parent_window_getter_;
93 base::Closure callback_;
94 scoped_ptr<ExtensionEnableFlow> flow_;
96 DISALLOW_COPY_AND_ASSIGN(EnableViaDialogFlow);
99 const Extension* GetExtension(const AppLaunchParams& params) {
100 if (params.extension_id.empty())
102 ExtensionRegistry* registry = ExtensionRegistry::Get(params.profile);
103 return registry->GetExtensionById(params.extension_id,
104 ExtensionRegistry::ENABLED |
105 ExtensionRegistry::DISABLED |
106 ExtensionRegistry::TERMINATED);
109 // Get the launch URL for a given extension, with optional override/fallback.
110 // |override_url|, if non-empty, will be preferred over the extension's
112 GURL UrlForExtension(const extensions::Extension* extension,
113 const GURL& override_url) {
118 if (!override_url.is_empty()) {
119 DCHECK(extension->web_extent().MatchesURL(override_url) ||
120 override_url.GetOrigin() == extension->url());
123 url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
126 // For extensions lacking launch urls, determine a reasonable fallback.
127 if (!url.is_valid()) {
128 url = extensions::OptionsPageInfo::GetOptionsPage(extension);
130 url = GURL(chrome::kChromeUIExtensionsURL);
136 WebContents* OpenEnabledApplication(const AppLaunchParams& params) {
137 const Extension* extension = GetExtension(params);
140 Profile* profile = params.profile;
142 WebContents* tab = NULL;
143 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile);
144 prefs->SetActiveBit(extension->id(), true);
146 UMA_HISTOGRAM_ENUMERATION(
147 "Extensions.AppLaunchContainer", params.container, 100);
149 if (CanLaunchViaEvent(extension)) {
150 // Remember what desktop the launch happened on so that when the app opens a
151 // window we can open them on the right desktop.
152 PerAppSettingsServiceFactory::GetForBrowserContext(profile)->
153 SetDesktopLastLaunchedFrom(extension->id(), params.desktop_type);
155 apps::LaunchPlatformAppWithCommandLine(profile,
158 params.current_directory,
163 // Record v1 app launch. Platform app launch is recorded when dispatching
164 // the onLaunched event.
165 prefs->SetLastLaunchTime(extension->id(), base::Time::Now());
167 GURL url = UrlForExtension(extension, params.override_url);
168 switch (params.container) {
169 case extensions::LAUNCH_CONTAINER_NONE: {
173 case extensions::LAUNCH_CONTAINER_PANEL:
174 case extensions::LAUNCH_CONTAINER_WINDOW:
175 tab = OpenWebAppWindow(params, url);
177 case extensions::LAUNCH_CONTAINER_TAB: {
178 tab = OpenWebAppTab(params, url);
190 WebContents* OpenApplication(const AppLaunchParams& params) {
191 return OpenEnabledApplication(params);
194 void OpenApplicationWithReenablePrompt(const AppLaunchParams& params) {
195 const Extension* extension = GetExtension(params);
198 Profile* profile = params.profile;
200 ExtensionService* service =
201 extensions::ExtensionSystem::Get(profile)->extension_service();
202 if (!service->IsExtensionEnabled(extension->id()) ||
203 extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
204 extension->id(), extensions::ExtensionRegistry::TERMINATED)) {
205 base::Callback<gfx::NativeWindow(void)> dialog_parent_window_getter;
206 // TODO(pkotwicz): Figure out which window should be used as the parent for
207 // the "enable application" dialog in Athena.
208 #if !defined(USE_ATHENA)
209 dialog_parent_window_getter =
210 base::Bind(&ShowAppListAndGetNativeWindow, params.desktop_type);
212 (new EnableViaDialogFlow(
213 service, profile, extension->id(), dialog_parent_window_getter,
214 base::Bind(base::IgnoreResult(OpenEnabledApplication), params)))->Run();
218 OpenEnabledApplication(params);
221 WebContents* OpenAppShortcutWindow(Profile* profile,
223 AppLaunchParams launch_params(
225 NULL, // this is a URL app. No extension.
226 extensions::LAUNCH_CONTAINER_WINDOW,
228 launch_params.override_url = url;
230 WebContents* tab = OpenWebAppWindow(launch_params, url);
235 extensions::TabHelper::FromWebContents(tab)->UpdateShortcutOnLoadComplete();
240 bool CanLaunchViaEvent(const extensions::Extension* extension) {
241 const extensions::FeatureProvider* feature_provider =
242 extensions::FeatureProvider::GetAPIFeatures();
243 extensions::Feature* feature = feature_provider->GetFeature("app.runtime");
244 return feature->IsAvailableToExtension(extension).is_available();