application_launch.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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. 4 5#include "chrome/browser/ui/extensions/application_launch.h" 6 7#include <string> 8 9#include "base/command_line.h" 10#include "base/metrics/histogram.h" 11#include "chrome/browser/app_mode/app_mode_utils.h" 12#include "chrome/browser/extensions/extension_prefs.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/extensions/extension_system.h" 15#include "chrome/browser/extensions/platform_app_launcher.h" 16#include "chrome/browser/extensions/tab_helper.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/ui/browser.h" 19#include "chrome/browser/ui/browser_commands.h" 20#include "chrome/browser/ui/browser_finder.h" 21#include "chrome/browser/ui/browser_tabstrip.h" 22#include "chrome/browser/ui/browser_window.h" 23#include "chrome/browser/ui/host_desktop.h" 24#include "chrome/browser/ui/tabs/tab_strip_model.h" 25#include "chrome/browser/web_applications/web_app.h" 26#include "chrome/common/chrome_switches.h" 27#include "chrome/common/extensions/extension.h" 28#include "chrome/common/extensions/extension_constants.h" 29#include "chrome/common/extensions/manifest_url_handler.h" 30#include "chrome/common/url_constants.h" 31#include "content/public/browser/render_view_host.h" 32#include "content/public/browser/web_contents.h" 33#include "content/public/browser/web_contents_view.h" 34#include "content/public/common/renderer_preferences.h" 35#include "ui/base/window_open_disposition.h" 36#include "ui/gfx/rect.h" 37 38#if defined(OS_MACOSX) 39#include "chrome/browser/ui/browser_commands_mac.h" 40#endif 41 42#if defined(OS_WIN) 43#include "win8/util/win8_util.h" 44#endif 45 46using content::WebContents; 47using extensions::Extension; 48using extensions::ExtensionPrefs; 49 50namespace { 51 52// Get the launch URL for a given extension, with optional override/fallback. 53// |override_url|, if non-empty, will be preferred over the extension's 54// launch url. 55GURL UrlForExtension(const Extension* extension, 56 const GURL& override_url) { 57 if (!extension) 58 return override_url; 59 60 GURL url; 61 if (!override_url.is_empty()) { 62 DCHECK(extension->web_extent().MatchesURL(override_url) || 63 override_url.GetOrigin() == extension->url()); 64 url = override_url; 65 } else { 66 url = extension->GetFullLaunchURL(); 67 } 68 69 // For extensions lacking launch urls, determine a reasonable fallback. 70 if (!url.is_valid()) { 71 url = extensions::ManifestURL::GetOptionsPage(extension); 72 if (!url.is_valid()) 73 url = GURL(chrome::kChromeUIExtensionsURL); 74 } 75 76 return url; 77} 78 79ui::WindowShowState DetermineWindowShowState( 80 Profile* profile, 81 extension_misc::LaunchContainer container, 82 const Extension* extension) { 83 if (!extension || 84 container != extension_misc::LAUNCH_WINDOW) { 85 return ui::SHOW_STATE_DEFAULT; 86 } 87 88 if (chrome::ShouldForceFullscreenApp()) 89 return ui::SHOW_STATE_FULLSCREEN; 90 91#if defined(USE_ASH) 92 // In ash, LAUNCH_FULLSCREEN launches in a maximized app window and 93 // LAUNCH_WINDOW launches in a normal app window. 94 ExtensionService* service = 95 extensions::ExtensionSystem::Get(profile)->extension_service(); 96 ExtensionPrefs::LaunchType launch_type = 97 service->extension_prefs()->GetLaunchType( 98 extension, ExtensionPrefs::LAUNCH_DEFAULT); 99 if (launch_type == ExtensionPrefs::LAUNCH_FULLSCREEN) 100 return ui::SHOW_STATE_MAXIMIZED; 101 else if (launch_type == ExtensionPrefs::LAUNCH_WINDOW) 102 return ui::SHOW_STATE_NORMAL; 103#endif 104 105 return ui::SHOW_STATE_DEFAULT; 106} 107 108WebContents* OpenApplicationWindow( 109 Profile* profile, 110 const Extension* extension, 111 extension_misc::LaunchContainer container, 112 const GURL& url_input, 113 Browser** app_browser, 114 const gfx::Rect& override_bounds) { 115 DCHECK(!url_input.is_empty() || extension); 116 GURL url = UrlForExtension(extension, url_input); 117 118 std::string app_name; 119 app_name = extension ? 120 web_app::GenerateApplicationNameFromExtensionId(extension->id()) : 121 web_app::GenerateApplicationNameFromURL(url); 122 123 Browser::Type type = Browser::TYPE_POPUP; 124 125 gfx::Rect window_bounds; 126 if (extension) { 127 window_bounds.set_width(extension->launch_width()); 128 window_bounds.set_height(extension->launch_height()); 129 } 130 if (!override_bounds.IsEmpty()) 131 window_bounds = override_bounds; 132 133 Browser::CreateParams params(type, profile, chrome::GetActiveDesktop()); 134 params.app_name = app_name; 135 params.initial_bounds = window_bounds; 136 params.initial_show_state = DetermineWindowShowState(profile, 137 container, 138 extension); 139 140 Browser* browser = NULL; 141#if defined(OS_WIN) 142 // On Windows 8's single window Metro mode we don't allow multiple Chrome 143 // windows to be created. We instead attempt to reuse an existing Browser 144 // window. 145 if (win8::IsSingleWindowMetroMode()) { 146 browser = chrome::FindBrowserWithProfile( 147 profile, chrome::HOST_DESKTOP_TYPE_NATIVE); 148 } 149#endif 150 if (!browser) 151 browser = new Browser(params); 152 153 if (app_browser) 154 *app_browser = browser; 155 156 WebContents* web_contents = chrome::AddSelectedTabWithURL( 157 browser, url, content::PAGE_TRANSITION_AUTO_TOPLEVEL); 158 web_contents->GetMutableRendererPrefs()->can_accept_load_drops = false; 159 web_contents->GetRenderViewHost()->SyncRendererPrefs(); 160 161 browser->window()->Show(); 162 163 // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial 164 // focus explicitly. 165 web_contents->GetView()->SetInitialFocus(); 166 return web_contents; 167} 168 169WebContents* OpenApplicationTab(Profile* profile, 170 const Extension* extension, 171 const GURL& override_url, 172 WindowOpenDisposition disposition) { 173 Browser* browser = chrome::FindTabbedBrowser(profile, 174 false, 175 chrome::GetActiveDesktop()); 176 WebContents* contents = NULL; 177 if (!browser) { 178 // No browser for this profile, need to open a new one. 179 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, 180 profile, 181 chrome::GetActiveDesktop())); 182 browser->window()->Show(); 183 // There's no current tab in this browser window, so add a new one. 184 disposition = NEW_FOREGROUND_TAB; 185 } else { 186 // For existing browser, ensure its window is shown and activated. 187 browser->window()->Show(); 188 browser->window()->Activate(); 189 } 190 191 // Check the prefs for overridden mode. 192 ExtensionService* extension_service = 193 extensions::ExtensionSystem::Get(profile)->extension_service(); 194 DCHECK(extension_service); 195 196 ExtensionPrefs::LaunchType launch_type = 197 extension_service->extension_prefs()->GetLaunchType( 198 extension, ExtensionPrefs::LAUNCH_DEFAULT); 199 UMA_HISTOGRAM_ENUMERATION("Extensions.AppTabLaunchType", launch_type, 100); 200 201 int add_type = TabStripModel::ADD_ACTIVE; 202 if (launch_type == ExtensionPrefs::LAUNCH_PINNED) 203 add_type |= TabStripModel::ADD_PINNED; 204 205 GURL extension_url = UrlForExtension(extension, override_url); 206 // TODO(erikkay): START_PAGE doesn't seem like the right transition in all 207 // cases. 208 chrome::NavigateParams params(browser, extension_url, 209 content::PAGE_TRANSITION_AUTO_TOPLEVEL); 210 params.tabstrip_add_types = add_type; 211 params.disposition = disposition; 212 213 if (disposition == CURRENT_TAB) { 214 WebContents* existing_tab = 215 browser->tab_strip_model()->GetActiveWebContents(); 216 TabStripModel* model = browser->tab_strip_model(); 217 int tab_index = model->GetIndexOfWebContents(existing_tab); 218 219 existing_tab->OpenURL(content::OpenURLParams( 220 extension_url, 221 content::Referrer(existing_tab->GetURL(), 222 WebKit::WebReferrerPolicyDefault), 223 disposition, content::PAGE_TRANSITION_LINK, false)); 224 // Reset existing_tab as OpenURL() may have clobbered it. 225 existing_tab = browser->tab_strip_model()->GetActiveWebContents(); 226 if (params.tabstrip_add_types & TabStripModel::ADD_PINNED) { 227 model->SetTabPinned(tab_index, true); 228 // Pinning may have moved the tab. 229 tab_index = model->GetIndexOfWebContents(existing_tab); 230 } 231 if (params.tabstrip_add_types & TabStripModel::ADD_ACTIVE) 232 model->ActivateTabAt(tab_index, true); 233 234 contents = existing_tab; 235 } else { 236 chrome::Navigate(¶ms); 237 contents = params.target_contents; 238 } 239 240 // On Chrome OS the host desktop type for a browser window is always set to 241 // HOST_DESKTOP_TYPE_ASH. On Windows 8 it is only the case for Chrome ASH 242 // in metro mode. 243 if (browser->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH) { 244 // In ash, LAUNCH_FULLSCREEN launches in the OpenApplicationWindow function 245 // i.e. it should not reach here. 246 DCHECK(launch_type != ExtensionPrefs::LAUNCH_FULLSCREEN); 247 } else { 248 // TODO(skerner): If we are already in full screen mode, and the user 249 // set the app to open as a regular or pinned tab, what should happen? 250 // Today we open the tab, but stay in full screen mode. Should we leave 251 // full screen mode in this case? 252 if (launch_type == ExtensionPrefs::LAUNCH_FULLSCREEN && 253 !browser->window()->IsFullscreen()) { 254#if defined(OS_MACOSX) 255 chrome::ToggleFullscreenWithChromeOrFallback(browser); 256#else 257 chrome::ToggleFullscreenMode(browser); 258#endif 259 } 260 } 261 return contents; 262} 263 264} // namespace 265 266namespace chrome { 267 268AppLaunchParams::AppLaunchParams(Profile* profile, 269 const extensions::Extension* extension, 270 extension_misc::LaunchContainer container, 271 WindowOpenDisposition disposition) 272 : profile(profile), 273 extension(extension), 274 container(container), 275 disposition(disposition), 276 override_url(), 277 override_bounds(), 278 command_line(NULL) {} 279 280AppLaunchParams::AppLaunchParams(Profile* profile, 281 const extensions::Extension* extension, 282 WindowOpenDisposition disposition) 283 : profile(profile), 284 extension(extension), 285 container(extension_misc::LAUNCH_NONE), 286 disposition(disposition), 287 override_url(), 288 override_bounds(), 289 command_line(NULL) { 290 ExtensionService* service = 291 extensions::ExtensionSystem::Get(profile)->extension_service(); 292 DCHECK(service); 293 294 // Look up the app preference to find out the right launch container. Default 295 // is to launch as a regular tab. 296 container = service->extension_prefs()->GetLaunchContainer( 297 extension, extensions::ExtensionPrefs::LAUNCH_REGULAR); 298} 299 300AppLaunchParams::AppLaunchParams(Profile* profile, 301 const extensions::Extension* extension, 302 int event_flags) 303 : profile(profile), 304 extension(extension), 305 container(extension_misc::LAUNCH_NONE), 306 disposition(ui::DispositionFromEventFlags(event_flags)), 307 override_url(), 308 override_bounds(), 309 command_line(NULL) { 310 if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { 311 container = extension_misc::LAUNCH_TAB; 312 } else if (disposition == NEW_WINDOW) { 313 container = extension_misc::LAUNCH_WINDOW; 314 } else { 315 ExtensionService* service = 316 extensions::ExtensionSystem::Get(profile)->extension_service(); 317 DCHECK(service); 318 319 // Look at preference to find the right launch container. If no preference 320 // is set, launch as a regular tab. 321 container = service->extension_prefs()->GetLaunchContainer( 322 extension, extensions::ExtensionPrefs::LAUNCH_DEFAULT); 323 disposition = NEW_FOREGROUND_TAB; 324 } 325} 326 327WebContents* OpenApplication(const AppLaunchParams& params) { 328 Profile* profile = params.profile; 329 const extensions::Extension* extension = params.extension; 330 extension_misc::LaunchContainer container = params.container; 331 const GURL& override_url = params.override_url; 332 const gfx::Rect& override_bounds = params.override_bounds; 333 334 WebContents* tab = NULL; 335 ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile)-> 336 extension_service()->extension_prefs(); 337 prefs->SetActiveBit(extension->id(), true); 338 339 UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunchContainer", container, 100); 340 341 if (extension->is_platform_app()) { 342 extensions::LaunchPlatformApp(profile, extension, params.command_line, 343 params.current_directory); 344 return NULL; 345 } 346 347 switch (container) { 348 case extension_misc::LAUNCH_NONE: { 349 NOTREACHED(); 350 break; 351 } 352 case extension_misc::LAUNCH_PANEL: 353 case extension_misc::LAUNCH_WINDOW: 354 tab = OpenApplicationWindow(profile, extension, container, 355 override_url, NULL, override_bounds); 356 break; 357 case extension_misc::LAUNCH_TAB: { 358 tab = OpenApplicationTab(profile, extension, override_url, 359 params.disposition); 360 break; 361 } 362 default: 363 NOTREACHED(); 364 break; 365 } 366 return tab; 367} 368 369WebContents* OpenAppShortcutWindow(Profile* profile, 370 const GURL& url, 371 const gfx::Rect& override_bounds) { 372 Browser* app_browser; 373 WebContents* tab = OpenApplicationWindow( 374 profile, 375 NULL, // this is a URL app. No extension. 376 extension_misc::LAUNCH_WINDOW, 377 url, 378 &app_browser, 379 override_bounds); 380 381 if (!tab) 382 return NULL; 383 384 // Set UPDATE_SHORTCUT as the pending web app action. This action is picked 385 // up in LoadingStateChanged to schedule a GetApplicationInfo. And when 386 // the web app info is available, extensions::TabHelper notifies Browser via 387 // OnDidGetApplicationInfo, which calls 388 // web_app::UpdateShortcutForTabContents when it sees UPDATE_SHORTCUT as 389 // pending web app action. 390 extensions::TabHelper::FromWebContents(tab)->set_pending_web_app_action( 391 extensions::TabHelper::UPDATE_SHORTCUT); 392 393 return tab; 394} 395 396} // namespace chrome 397