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"
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/browser/extensions/extension_prefs.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/platform_app_launcher.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_commands.h"
16 #include "chrome/browser/ui/browser_finder.h"
17 #include "chrome/browser/ui/browser_tabstrip.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/tab_contents/tab_contents.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/browser/web_applications/web_app.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/extensions/extension.h"
24 #include "chrome/common/extensions/extension_constants.h"
25 #include "chrome/common/url_constants.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_view.h"
29 #include "content/public/common/renderer_preferences.h"
30 #include "ui/gfx/rect.h"
33 #include "base/win/metro.h"
36 using content::WebContents
;
37 using extensions::Extension
;
38 using extensions::ExtensionPrefs
;
42 // Get the launch URL for a given extension, with optional override/fallback.
43 // |override_url|, if non-empty, will be preferred over the extension's
45 GURL
UrlForExtension(const Extension
* extension
,
46 const GURL
& override_url
) {
51 if (!override_url
.is_empty()) {
52 DCHECK(extension
->web_extent().MatchesURL(override_url
) ||
53 override_url
.GetOrigin() == extension
->url());
56 url
= extension
->GetFullLaunchURL();
59 // For extensions lacking launch urls, determine a reasonable fallback.
60 if (!url
.is_valid()) {
61 url
= extension
->options_url();
63 url
= GURL(chrome::kChromeUIExtensionsURL
);
69 WebContents
* OpenApplicationWindow(
71 const Extension
* extension
,
72 extension_misc::LaunchContainer container
,
73 const GURL
& url_input
,
74 Browser
** app_browser
,
75 const gfx::Rect
& override_bounds
) {
76 DCHECK(!url_input
.is_empty() || extension
);
77 GURL url
= UrlForExtension(extension
, url_input
);
80 app_name
= extension
?
81 web_app::GenerateApplicationNameFromExtensionId(extension
->id()) :
82 web_app::GenerateApplicationNameFromURL(url
);
84 Browser::Type type
= Browser::TYPE_POPUP
;
86 gfx::Rect window_bounds
;
88 window_bounds
.set_width(extension
->launch_width());
89 window_bounds
.set_height(extension
->launch_height());
90 } else if (!override_bounds
.IsEmpty()) {
91 window_bounds
= override_bounds
;
94 Browser::CreateParams
params(type
, profile
);
95 params
.app_name
= app_name
;
96 params
.initial_bounds
= window_bounds
;
100 container
== extension_misc::LAUNCH_WINDOW
) {
101 // In ash, LAUNCH_FULLSCREEN launches in a maximized app window and
102 // LAUNCH_WINDOW launches in a normal app window.
103 ExtensionPrefs::LaunchType launch_type
=
104 profile
->GetExtensionService()->extension_prefs()->GetLaunchType(
105 extension
->id(), ExtensionPrefs::LAUNCH_DEFAULT
);
106 if (launch_type
== ExtensionPrefs::LAUNCH_FULLSCREEN
)
107 params
.initial_show_state
= ui::SHOW_STATE_MAXIMIZED
;
108 else if (launch_type
== ExtensionPrefs::LAUNCH_WINDOW
)
109 params
.initial_show_state
= ui::SHOW_STATE_NORMAL
;
113 Browser
* browser
= NULL
;
115 // In Chrome on Windows 8 in metro mode we don't allow multiple chrome
116 // windows to be created, as we don't have a good way to switch between
117 // them. We attempt to reuse an existing Browser window.
118 if (base::win::IsMetroProcess()) {
119 browser
= browser::FindBrowserWithProfile(
120 profile
, chrome::HOST_DESKTOP_TYPE_NATIVE
);
124 browser
= new Browser(params
);
127 *app_browser
= browser
;
129 WebContents
* web_contents
= chrome::AddSelectedTabWithURL(
130 browser
, url
, content::PAGE_TRANSITION_AUTO_TOPLEVEL
);
131 web_contents
->GetMutableRendererPrefs()->can_accept_load_drops
= false;
132 web_contents
->GetRenderViewHost()->SyncRendererPrefs();
134 browser
->window()->Show();
136 // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial
138 web_contents
->GetView()->SetInitialFocus();
142 WebContents
* OpenApplicationTab(Profile
* profile
,
143 const Extension
* extension
,
144 const GURL
& override_url
,
145 WindowOpenDisposition disposition
) {
146 Browser
* browser
= browser::FindTabbedBrowserDeprecated(profile
, false);
147 WebContents
* contents
= NULL
;
149 // No browser for this profile, need to open a new one.
150 browser
= new Browser(Browser::CreateParams(profile
));
151 browser
->window()->Show();
152 // There's no current tab in this browser window, so add a new one.
153 disposition
= NEW_FOREGROUND_TAB
;
155 // For existing browser, ensure its window is activated.
156 browser
->window()->Activate();
159 // Check the prefs for overridden mode.
160 ExtensionService
* extension_service
= profile
->GetExtensionService();
161 DCHECK(extension_service
);
163 ExtensionPrefs::LaunchType launch_type
=
164 extension_service
->extension_prefs()->GetLaunchType(
165 extension
->id(), ExtensionPrefs::LAUNCH_DEFAULT
);
166 UMA_HISTOGRAM_ENUMERATION("Extensions.AppTabLaunchType", launch_type
, 100);
168 int add_type
= TabStripModel::ADD_ACTIVE
;
169 if (launch_type
== ExtensionPrefs::LAUNCH_PINNED
)
170 add_type
|= TabStripModel::ADD_PINNED
;
172 GURL extension_url
= UrlForExtension(extension
, override_url
);
173 // TODO(erikkay): START_PAGE doesn't seem like the right transition in all
175 chrome::NavigateParams
params(browser
, extension_url
,
176 content::PAGE_TRANSITION_AUTO_TOPLEVEL
);
177 params
.tabstrip_add_types
= add_type
;
178 params
.disposition
= disposition
;
180 if (disposition
== CURRENT_TAB
) {
181 WebContents
* existing_tab
= chrome::GetActiveWebContents(browser
);
182 TabStripModel
* model
= browser
->tab_strip_model();
183 int tab_index
= model
->GetIndexOfWebContents(existing_tab
);
185 existing_tab
->OpenURL(content::OpenURLParams(
187 content::Referrer(existing_tab
->GetURL(),
188 WebKit::WebReferrerPolicyDefault
),
189 disposition
, content::PAGE_TRANSITION_LINK
, false));
190 // Reset existing_tab as OpenURL() may have clobbered it.
191 existing_tab
= chrome::GetActiveWebContents(browser
);
192 if (params
.tabstrip_add_types
& TabStripModel::ADD_PINNED
) {
193 model
->SetTabPinned(tab_index
, true);
194 // Pinning may have moved the tab.
195 tab_index
= model
->GetIndexOfWebContents(existing_tab
);
197 if (params
.tabstrip_add_types
& TabStripModel::ADD_ACTIVE
)
198 model
->ActivateTabAt(tab_index
, true);
200 contents
= existing_tab
;
202 chrome::Navigate(¶ms
);
203 contents
= params
.target_contents
->web_contents();
207 // In ash, LAUNCH_FULLSCREEN launches in a maximized app window and it should
209 DCHECK(launch_type
!= ExtensionPrefs::LAUNCH_FULLSCREEN
);
211 // TODO(skerner): If we are already in full screen mode, and the user
212 // set the app to open as a regular or pinned tab, what should happen?
213 // Today we open the tab, but stay in full screen mode. Should we leave
214 // full screen mode in this case?
215 if (launch_type
== ExtensionPrefs::LAUNCH_FULLSCREEN
&&
216 !browser
->window()->IsFullscreen()) {
217 chrome::ToggleFullscreenMode(browser
);
226 namespace application_launch
{
228 LaunchParams::LaunchParams(Profile
* profile
,
229 const extensions::Extension
* extension
,
230 extension_misc::LaunchContainer container
,
231 WindowOpenDisposition disposition
)
233 extension(extension
),
234 container(container
),
235 disposition(disposition
),
237 command_line(NULL
) {}
239 WebContents
* OpenApplication(const LaunchParams
& params
) {
240 Profile
* profile
= params
.profile
;
241 const extensions::Extension
* extension
= params
.extension
;
242 extension_misc::LaunchContainer container
= params
.container
;
243 const GURL
& override_url
= params
.override_url
;
245 WebContents
* tab
= NULL
;
246 ExtensionPrefs
* prefs
= profile
->GetExtensionService()->extension_prefs();
247 prefs
->SetActiveBit(extension
->id(), true);
249 UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunchContainer", container
, 100);
251 if (extension
->is_platform_app()) {
252 extensions::LaunchPlatformApp(profile
, extension
, params
.command_line
,
253 params
.current_directory
);
258 case extension_misc::LAUNCH_NONE
: {
262 case extension_misc::LAUNCH_PANEL
:
263 case extension_misc::LAUNCH_WINDOW
:
264 tab
= OpenApplicationWindow(profile
, extension
, container
,
265 override_url
, NULL
, gfx::Rect());
267 case extension_misc::LAUNCH_TAB
: {
268 tab
= OpenApplicationTab(profile
, extension
, override_url
,
279 WebContents
* OpenAppShortcutWindow(Profile
* profile
,
281 const gfx::Rect
& override_bounds
) {
282 Browser
* app_browser
;
283 WebContents
* tab
= OpenApplicationWindow(
285 NULL
, // this is a URL app. No extension.
286 extension_misc::LAUNCH_WINDOW
,
294 // Set UPDATE_SHORTCUT as the pending web app action. This action is picked
295 // up in LoadingStateChanged to schedule a GetApplicationInfo. And when
296 // the web app info is available, extensions::TabHelper notifies Browser via
297 // OnDidGetApplicationInfo, which calls
298 // web_app::UpdateShortcutForTabContents when it sees UPDATE_SHORTCUT as
299 // pending web app action.
300 extensions::TabHelper::FromWebContents(tab
)->set_pending_web_app_action(
301 extensions::TabHelper::UPDATE_SHORTCUT
);
306 } // namespace application_launch