startup_browser_creator_impl.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/startup/startup_browser_creator_impl.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "apps/app_restore_service.h"
11#include "apps/app_restore_service_factory.h"
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/command_line.h"
15#include "base/compiler_specific.h"
16#include "base/environment.h"
17#include "base/lazy_instance.h"
18#include "base/memory/scoped_ptr.h"
19#include "base/metrics/histogram.h"
20#include "base/metrics/statistics_recorder.h"
21#include "base/path_service.h"
22#include "base/prefs/pref_service.h"
23#include "base/strings/string_number_conversions.h"
24#include "base/strings/string_split.h"
25#include "base/strings/utf_string_conversions.h"
26#include "base/threading/thread_restrictions.h"
27#include "chrome/browser/auto_launch_trial.h"
28#include "chrome/browser/browser_process.h"
29#include "chrome/browser/chrome_notification_types.h"
30#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
31#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
32#include "chrome/browser/defaults.h"
33#include "chrome/browser/extensions/extension_creator.h"
34#include "chrome/browser/extensions/extension_service.h"
35#include "chrome/browser/extensions/extension_system.h"
36#include "chrome/browser/extensions/pack_extension_job.h"
37#include "chrome/browser/first_run/first_run.h"
38#include "chrome/browser/google/google_util.h"
39#include "chrome/browser/infobars/infobar_service.h"
40#include "chrome/browser/net/predictor.h"
41#include "chrome/browser/notifications/desktop_notification_service.h"
42#include "chrome/browser/performance_monitor/startup_timer.h"
43#include "chrome/browser/prefs/incognito_mode_prefs.h"
44#include "chrome/browser/prefs/session_startup_pref.h"
45#include "chrome/browser/profiles/profile.h"
46#include "chrome/browser/profiles/profile_io_data.h"
47#include "chrome/browser/rlz/rlz.h"
48#include "chrome/browser/sessions/session_restore.h"
49#include "chrome/browser/sessions/session_service.h"
50#include "chrome/browser/sessions/session_service_factory.h"
51#include "chrome/browser/shell_integration.h"
52#include "chrome/browser/signin/signin_promo.h"
53#include "chrome/browser/ui/app_list/app_list_service.h"
54#include "chrome/browser/ui/browser_commands.h"
55#include "chrome/browser/ui/browser_finder.h"
56#include "chrome/browser/ui/browser_list.h"
57#include "chrome/browser/ui/browser_navigator.h"
58#include "chrome/browser/ui/browser_tabrestore.h"
59#include "chrome/browser/ui/browser_tabstrip.h"
60#include "chrome/browser/ui/browser_window.h"
61#include "chrome/browser/ui/extensions/application_launch.h"
62#include "chrome/browser/ui/host_desktop.h"
63#include "chrome/browser/ui/startup/autolaunch_prompt.h"
64#include "chrome/browser/ui/startup/bad_flags_prompt.h"
65#include "chrome/browser/ui/startup/default_browser_prompt.h"
66#include "chrome/browser/ui/startup/google_api_keys_infobar_delegate.h"
67#include "chrome/browser/ui/startup/obsolete_os_infobar_delegate.h"
68#include "chrome/browser/ui/startup/session_crashed_infobar_delegate.h"
69#include "chrome/browser/ui/startup/startup_browser_creator.h"
70#include "chrome/browser/ui/tabs/pinned_tab_codec.h"
71#include "chrome/browser/ui/tabs/tab_strip_model.h"
72#include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
73#include "chrome/common/chrome_constants.h"
74#include "chrome/common/chrome_paths.h"
75#include "chrome/common/chrome_result_codes.h"
76#include "chrome/common/chrome_switches.h"
77#include "chrome/common/chrome_version_info.h"
78#include "chrome/common/extensions/extension_constants.h"
79#include "chrome/common/net/url_fixer_upper.h"
80#include "chrome/common/pref_names.h"
81#include "chrome/common/url_constants.h"
82#include "chrome/installer/util/browser_distribution.h"
83#include "content/public/browser/child_process_security_policy.h"
84#include "content/public/browser/dom_storage_context.h"
85#include "content/public/browser/notification_observer.h"
86#include "content/public/browser/notification_registrar.h"
87#include "content/public/browser/storage_partition.h"
88#include "content/public/browser/web_contents.h"
89#include "content/public/browser/web_contents_view.h"
90#include "grit/locale_settings.h"
91#include "ui/base/l10n/l10n_util.h"
92#include "ui/base/resource/resource_bundle.h"
93#include "ui/gfx/rect.h"
94#include "ui/gfx/screen.h"
95
96#if defined(OS_MACOSX)
97#include "base/mac/mac_util.h"
98#include "chrome/browser/ui/cocoa/keystone_infobar_delegate.h"
99#endif
100
101#if defined(TOOLKIT_GTK)
102#include "chrome/browser/ui/gtk/gtk_util.h"
103#endif
104
105#if defined(OS_WIN)
106#include "apps/app_launch_for_metro_restart_win.h"
107#include "base/win/windows_version.h"
108#endif
109
110using content::ChildProcessSecurityPolicy;
111using content::WebContents;
112using extensions::Extension;
113
114namespace {
115
116// Utility functions ----------------------------------------------------------
117
118enum LaunchMode {
119  LM_TO_BE_DECIDED = 0,       // Possibly direct launch or via a shortcut.
120  LM_AS_WEBAPP,               // Launched as a installed web application.
121  LM_WITH_URLS,               // Launched with urls in the cmd line.
122  LM_SHORTCUT_NONE,           // Not launched from a shortcut.
123  LM_SHORTCUT_NONAME,         // Launched from shortcut but no name available.
124  LM_SHORTCUT_UNKNOWN,        // Launched from user-defined shortcut.
125  LM_SHORTCUT_QUICKLAUNCH,    // Launched from the quick launch bar.
126  LM_SHORTCUT_DESKTOP,        // Launched from a desktop shortcut.
127  LM_SHORTCUT_TASKBAR,        // Launched from the taskbar.
128  LM_LINUX_MAC_BEOS           // Other OS buckets start here.
129};
130
131#if defined(OS_WIN)
132// Undocumented flag in the startup info structure tells us what shortcut was
133// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for
134// more information. Confirmed to work on XP, Vista and Win7.
135LaunchMode GetLaunchShortcutKind() {
136  STARTUPINFOW si = { sizeof(si) };
137  GetStartupInfoW(&si);
138  if (si.dwFlags & 0x800) {
139    if (!si.lpTitle)
140      return LM_SHORTCUT_NONAME;
141    string16 shortcut(si.lpTitle);
142    // The windows quick launch path is not localized.
143    if (shortcut.find(L"\\Quick Launch\\") != string16::npos) {
144      if (base::win::GetVersion() >= base::win::VERSION_WIN7)
145        return LM_SHORTCUT_TASKBAR;
146      else
147        return LM_SHORTCUT_QUICKLAUNCH;
148    }
149    scoped_ptr<base::Environment> env(base::Environment::Create());
150    std::string appdata_path;
151    env->GetVar("USERPROFILE", &appdata_path);
152    if (!appdata_path.empty() &&
153        shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos)
154      return LM_SHORTCUT_DESKTOP;
155    return LM_SHORTCUT_UNKNOWN;
156  }
157  return LM_SHORTCUT_NONE;
158}
159#else
160// TODO(cpu): Port to other platforms.
161LaunchMode GetLaunchShortcutKind() {
162  return LM_LINUX_MAC_BEOS;
163}
164#endif
165
166// Log in a histogram the frequency of launching by the different methods. See
167// LaunchMode enum for the actual values of the buckets.
168void RecordLaunchModeHistogram(LaunchMode mode) {
169  int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode;
170  UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket);
171}
172
173void UrlsToTabs(const std::vector<GURL>& urls, StartupTabs* tabs) {
174  for (size_t i = 0; i < urls.size(); ++i) {
175    StartupTab tab;
176    tab.is_pinned = false;
177    tab.url = urls[i];
178    tabs->push_back(tab);
179  }
180}
181
182// Return true if the command line option --app-id is used.  Set
183// |out_extension| to the app to open, and |out_launch_container|
184// to the type of window into which the app should be open.
185bool GetAppLaunchContainer(
186    Profile* profile,
187    const std::string& app_id,
188    const Extension** out_extension,
189    extension_misc::LaunchContainer* out_launch_container) {
190
191  ExtensionService* extensions_service = profile->GetExtensionService();
192  const Extension* extension =
193      extensions_service->GetExtensionById(app_id, false);
194
195  // The extension with id |app_id| may have been uninstalled.
196  if (!extension)
197    return false;
198
199  // Don't launch platform apps in incognito mode.
200  if (profile->IsOffTheRecord() && extension->is_platform_app())
201    return false;
202
203  // Look at preferences to find the right launch container.  If no
204  // preference is set, launch as a window.
205  extension_misc::LaunchContainer launch_container =
206      extensions_service->extension_prefs()->GetLaunchContainer(
207          extension, extensions::ExtensionPrefs::LAUNCH_WINDOW);
208
209  *out_extension = extension;
210  *out_launch_container = launch_container;
211  return true;
212}
213
214// Parse two comma-separated integers from string. Return true on success.
215bool ParseCommaSeparatedIntegers(const std::string& str,
216                                 int* ret_num1,
217                                 int* ret_num2) {
218  std::vector<std::string> dimensions;
219  base::SplitString(str, ',', &dimensions);
220  if (dimensions.size() != 2)
221    return false;
222
223  int num1, num2;
224  if (!base::StringToInt(dimensions[0], &num1) ||
225      !base::StringToInt(dimensions[1], &num2))
226    return false;
227
228  *ret_num1 = num1;
229  *ret_num2 = num2;
230  return true;
231}
232
233void RecordCmdLineAppHistogram(extensions::Manifest::Type app_type) {
234  CoreAppLauncherHandler::RecordAppLaunchType(
235      extension_misc::APP_LAUNCH_CMD_LINE_APP,
236      app_type);
237}
238
239void RecordAppLaunches(Profile* profile,
240                       const std::vector<GURL>& cmd_line_urls,
241                       StartupTabs& autolaunch_tabs) {
242  ExtensionService* extension_service = profile->GetExtensionService();
243  DCHECK(extension_service);
244  for (size_t i = 0; i < cmd_line_urls.size(); ++i) {
245    const extensions::Extension* extension =
246        extension_service->GetInstalledApp(cmd_line_urls.at(i));
247    if (extension) {
248      CoreAppLauncherHandler::RecordAppLaunchType(
249          extension_misc::APP_LAUNCH_CMD_LINE_URL,
250          extension->GetType());
251    }
252  }
253  for (size_t i = 0; i < autolaunch_tabs.size(); ++i) {
254    const extensions::Extension* extension =
255        extension_service->GetInstalledApp(autolaunch_tabs.at(i).url);
256    if (extension) {
257      CoreAppLauncherHandler::RecordAppLaunchType(
258          extension_misc::APP_LAUNCH_AUTOLAUNCH,
259          extension->GetType());
260    }
261  }
262}
263
264bool IsNewTabURL(Profile* profile, const GURL& url) {
265  GURL ntp_url(chrome::kChromeUINewTabURL);
266  return url == ntp_url ||
267         (url.is_empty() && profile->GetHomePage() == ntp_url);
268}
269
270class WebContentsCloseObserver : public content::NotificationObserver {
271 public:
272  WebContentsCloseObserver() : contents_(NULL) {}
273  virtual ~WebContentsCloseObserver() {}
274
275  void SetContents(content::WebContents* contents) {
276    DCHECK(!contents_);
277    contents_ = contents;
278
279    registrar_.Add(this,
280                   content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
281                   content::Source<content::WebContents>(contents_));
282  }
283
284  content::WebContents* contents() { return contents_; }
285
286 private:
287  // content::NotificationObserver overrides:
288  virtual void Observe(int type,
289                       const content::NotificationSource& source,
290                       const content::NotificationDetails& details) OVERRIDE {
291    DCHECK_EQ(type, content::NOTIFICATION_WEB_CONTENTS_DESTROYED);
292    contents_ = NULL;
293  }
294
295  content::WebContents* contents_;
296  content::NotificationRegistrar registrar_;
297
298  DISALLOW_COPY_AND_ASSIGN(WebContentsCloseObserver);
299};
300
301}  // namespace
302
303namespace internals {
304
305GURL GetWelcomePageURL() {
306  std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL);
307  return GURL(welcome_url);
308}
309
310}  // namespace internals
311
312StartupBrowserCreatorImpl::StartupBrowserCreatorImpl(
313    const base::FilePath& cur_dir,
314    const CommandLine& command_line,
315    chrome::startup::IsFirstRun is_first_run)
316    : cur_dir_(cur_dir),
317      command_line_(command_line),
318      profile_(NULL),
319      browser_creator_(NULL),
320      is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) {
321}
322
323StartupBrowserCreatorImpl::StartupBrowserCreatorImpl(
324    const base::FilePath& cur_dir,
325    const CommandLine& command_line,
326    StartupBrowserCreator* browser_creator,
327    chrome::startup::IsFirstRun is_first_run)
328    : cur_dir_(cur_dir),
329      command_line_(command_line),
330      profile_(NULL),
331      browser_creator_(browser_creator),
332      is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) {
333}
334
335StartupBrowserCreatorImpl::~StartupBrowserCreatorImpl() {
336}
337
338bool StartupBrowserCreatorImpl::Launch(Profile* profile,
339                                       const std::vector<GURL>& urls_to_open,
340                                       bool process_startup,
341                                       chrome::HostDesktopType desktop_type) {
342  DCHECK(profile);
343  profile_ = profile;
344
345  if (command_line_.HasSwitch(switches::kDnsLogDetails))
346    chrome_browser_net::EnablePredictorDetailedLog(true);
347  if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) &&
348      profile->GetNetworkPredictor()) {
349    profile->GetNetworkPredictor()->EnablePredictor(false);
350  }
351
352  AppListService::InitAll(profile);
353  if (command_line_.HasSwitch(switches::kShowAppList)) {
354    AppListService::RecordShowTimings(command_line_);
355    AppListService::Get()->ShowForProfile(profile);
356    return true;
357  }
358
359  // Open the required browser windows and tabs. First, see if
360  // we're being run as an application window. If so, the user
361  // opened an app shortcut.  Don't restore tabs or open initial
362  // URLs in that case. The user should see the window as an app,
363  // not as chrome.
364  // Special case is when app switches are passed but we do want to restore
365  // session. In that case open app window + focus it after session is restored.
366  content::WebContents* app_contents = NULL;
367  if (OpenApplicationWindow(profile, &app_contents)) {
368    RecordLaunchModeHistogram(LM_AS_WEBAPP);
369  } else {
370    RecordLaunchModeHistogram(urls_to_open.empty() ?
371                              LM_TO_BE_DECIDED : LM_WITH_URLS);
372
373    ProcessLaunchURLs(process_startup, urls_to_open, desktop_type);
374
375    // If this is an app launch, but we didn't open an app window, it may
376    // be an app tab.
377    OpenApplicationTab(profile);
378
379#if defined(OS_MACOSX)
380    if (process_startup) {
381      // Check whether the auto-update system needs to be promoted from user
382      // to system.
383      KeystoneInfoBar::PromotionInfoBar(profile);
384    }
385#endif
386  }
387
388#if defined(OS_WIN)
389  if (process_startup)
390    ShellIntegration::MigrateChromiumShortcuts();
391#endif  // defined(OS_WIN)
392
393#if defined(ENABLE_EXTENSIONS)
394  // If we deferred creation of background extension hosts, we want to create
395  // them now that the session (if any) has been restored.
396  ExtensionProcessManager* process_manager =
397      extensions::ExtensionSystem::Get(profile)->process_manager();
398  process_manager->DeferBackgroundHostCreation(false);
399#endif
400
401  return true;
402}
403
404void StartupBrowserCreatorImpl::ExtractOptionalAppWindowSize(
405    gfx::Rect* bounds) {
406  if (command_line_.HasSwitch(switches::kAppWindowSize)) {
407    int width, height;
408    width = height = 0;
409    std::string switch_value =
410        command_line_.GetSwitchValueASCII(switches::kAppWindowSize);
411    if (ParseCommaSeparatedIntegers(switch_value, &width, &height)) {
412      // TODO(scottmg): NativeScreen might be wrong. http://crbug.com/133312
413      const gfx::Rect work_area =
414          gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
415      width = std::min(width, work_area.width());
416      height = std::min(height, work_area.height());
417      bounds->set_size(gfx::Size(width, height));
418      bounds->set_x((work_area.width() - bounds->width()) / 2);
419      // TODO(nkostylev): work_area does include launcher but should not.
420      // Launcher auto hide pref is synced and is most likely not applied here.
421      bounds->set_y((work_area.height() - bounds->height()) / 2);
422    }
423  }
424}
425
426bool StartupBrowserCreatorImpl::IsAppLaunch(std::string* app_url,
427                                            std::string* app_id) {
428  if (command_line_.HasSwitch(switches::kApp)) {
429    if (app_url)
430      *app_url = command_line_.GetSwitchValueASCII(switches::kApp);
431    return true;
432  }
433  if (command_line_.HasSwitch(switches::kAppId)) {
434    if (app_id)
435      *app_id = command_line_.GetSwitchValueASCII(switches::kAppId);
436    return true;
437  }
438  return false;
439}
440
441bool StartupBrowserCreatorImpl::OpenApplicationTab(Profile* profile) {
442  std::string app_id;
443  // App shortcuts to URLs always open in an app window.  Because this
444  // function will open an app that should be in a tab, there is no need
445  // to look at the app URL.  OpenApplicationWindow() will open app url
446  // shortcuts.
447  if (!IsAppLaunch(NULL, &app_id) || app_id.empty())
448    return false;
449
450  extension_misc::LaunchContainer launch_container;
451  const Extension* extension;
452  if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
453    return false;
454
455  // If the user doesn't want to open a tab, fail.
456  if (launch_container != extension_misc::LAUNCH_TAB)
457    return false;
458
459  RecordCmdLineAppHistogram(extension->GetType());
460
461  WebContents* app_tab = chrome::OpenApplication(chrome::AppLaunchParams(
462      profile, extension, extension_misc::LAUNCH_TAB, NEW_FOREGROUND_TAB));
463  return (app_tab != NULL);
464}
465
466bool StartupBrowserCreatorImpl::OpenApplicationWindow(
467    Profile* profile,
468    content::WebContents** out_app_contents) {
469  // Set |out_app_contents| to NULL early on (just in case).
470  if (out_app_contents)
471    *out_app_contents = NULL;
472
473  std::string url_string, app_id;
474  if (!IsAppLaunch(&url_string, &app_id))
475    return false;
476
477  // This can fail if the app_id is invalid.  It can also fail if the
478  // extension is external, and has not yet been installed.
479  // TODO(skerner): Do something reasonable here. Pop up a warning panel?
480  // Open an URL to the gallery page of the extension id?
481  if (!app_id.empty()) {
482    extension_misc::LaunchContainer launch_container;
483    const Extension* extension;
484    if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
485      return false;
486
487    // TODO(skerner): Could pass in |extension| and |launch_container|,
488    // and avoid calling GetAppLaunchContainer() both here and in
489    // OpenApplicationTab().
490
491    if (launch_container == extension_misc::LAUNCH_TAB)
492      return false;
493
494    RecordCmdLineAppHistogram(extension->GetType());
495
496    chrome::AppLaunchParams params(profile, extension,
497                                   launch_container, NEW_WINDOW);
498    params.command_line = &command_line_;
499    params.current_directory = cur_dir_;
500    WebContents* tab_in_app_window = chrome::OpenApplication(params);
501
502    if (out_app_contents)
503      *out_app_contents = tab_in_app_window;
504
505    // Platform apps fire off a launch event which may or may not open a window.
506    return (tab_in_app_window != NULL || extension->is_platform_app());
507  }
508
509  if (url_string.empty())
510    return false;
511
512#if defined(OS_WIN)  // Fix up Windows shortcuts.
513  ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%");
514#endif
515  GURL url(url_string);
516
517  // Restrict allowed URLs for --app switch.
518  if (!url.is_empty() && url.is_valid()) {
519    ChildProcessSecurityPolicy* policy =
520        ChildProcessSecurityPolicy::GetInstance();
521    if (policy->IsWebSafeScheme(url.scheme()) ||
522        url.SchemeIs(chrome::kFileScheme)) {
523      const extensions::Extension* extension =
524          profile->GetExtensionService()->GetInstalledApp(url);
525      if (extension) {
526        RecordCmdLineAppHistogram(extension->GetType());
527      } else {
528        CoreAppLauncherHandler::RecordAppLaunchType(
529            extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY,
530            extensions::Manifest::TYPE_HOSTED_APP);
531      }
532
533      gfx::Rect override_bounds;
534      ExtractOptionalAppWindowSize(&override_bounds);
535
536      WebContents* app_tab = chrome::OpenAppShortcutWindow(profile,
537                                                           url,
538                                                           override_bounds);
539
540      if (out_app_contents)
541        *out_app_contents = app_tab;
542
543      return (app_tab != NULL);
544    }
545  }
546  return false;
547}
548
549void StartupBrowserCreatorImpl::ProcessLaunchURLs(
550    bool process_startup,
551    const std::vector<GURL>& urls_to_open,
552    chrome::HostDesktopType desktop_type) {
553  // If we're starting up in "background mode" (no open browser window) then
554  // don't open any browser windows, unless kAutoLaunchAtStartup is also
555  // specified.
556  if (process_startup &&
557      command_line_.HasSwitch(switches::kNoStartupWindow) &&
558      !command_line_.HasSwitch(switches::kAutoLaunchAtStartup)) {
559    return;
560  }
561
562// TODO(tapted): Move this to startup_browser_creator_win.cc after refactor.
563#if defined(OS_WIN)
564  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
565    // See if there are apps for this profile that should be launched on startup
566    // due to a switch from Metro mode.
567    apps::HandleAppLaunchForMetroRestart(profile_);
568  }
569#endif
570
571  if (process_startup && ProcessStartupURLs(urls_to_open, desktop_type)) {
572    // ProcessStartupURLs processed the urls, nothing else to do.
573    return;
574  }
575
576  chrome::startup::IsProcessStartup is_process_startup = process_startup ?
577      chrome::startup::IS_PROCESS_STARTUP :
578      chrome::startup::IS_NOT_PROCESS_STARTUP;
579  if (!process_startup) {
580    // Even if we're not starting a new process, this may conceptually be
581    // "startup" for the user and so should be handled in a similar way.  Eg.,
582    // Chrome may have been running in the background due to an app with a
583    // background page being installed, or running with only an app window
584    // displayed.
585    SessionService* service = SessionServiceFactory::GetForProfile(profile_);
586    if (service && service->ShouldNewWindowStartSession()) {
587      // Restore the last session if any.
588      if (!HasPendingUncleanExit(profile_) &&
589          service->RestoreIfNecessary(urls_to_open)) {
590        return;
591      }
592      // Open user-specified URLs like pinned tabs and startup tabs.
593      Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type);
594      if (browser) {
595        AddInfoBarsIfNecessary(browser, is_process_startup);
596        return;
597      }
598    }
599  }
600
601  // Session startup didn't occur, open the urls.
602  Browser* browser = NULL;
603  std::vector<GURL> adjust_urls = urls_to_open;
604  if (adjust_urls.empty()) {
605    AddStartupURLs(&adjust_urls);
606    if (StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser(
607            profile_, adjust_urls))
608      return;
609  } else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) {
610    // Always open a list of urls in a window on the native desktop.
611    browser = chrome::FindTabbedBrowser(profile_, false,
612                                        chrome::HOST_DESKTOP_TYPE_NATIVE);
613  }
614  // This will launch a browser; prevent session restore.
615  StartupBrowserCreator::in_synchronous_profile_launch_ = true;
616  browser = OpenURLsInBrowser(browser, process_startup, adjust_urls,
617                              desktop_type);
618  StartupBrowserCreator::in_synchronous_profile_launch_ = false;
619  AddInfoBarsIfNecessary(browser, is_process_startup);
620}
621
622bool StartupBrowserCreatorImpl::ProcessStartupURLs(
623    const std::vector<GURL>& urls_to_open,
624    chrome::HostDesktopType desktop_type) {
625  VLOG(1) << "StartupBrowserCreatorImpl::ProcessStartupURLs";
626  SessionStartupPref pref =
627      StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_);
628  if (pref.type == SessionStartupPref::LAST)
629    VLOG(1) << "Pref: last";
630  else if (pref.type == SessionStartupPref::URLS)
631    VLOG(1) << "Pref: urls";
632  else if (pref.type == SessionStartupPref::DEFAULT)
633    VLOG(1) << "Pref: default";
634
635  apps::AppRestoreService* restore_service =
636      apps::AppRestoreServiceFactory::GetForProfile(profile_);
637  // NULL in incognito mode.
638  if (restore_service) {
639    restore_service->HandleStartup(apps::AppRestoreService::ShouldRestoreApps(
640        StartupBrowserCreator::WasRestarted()));
641  }
642
643  if (pref.type == SessionStartupPref::LAST) {
644    if (profile_->GetLastSessionExitType() == Profile::EXIT_CRASHED &&
645        !command_line_.HasSwitch(switches::kRestoreLastSession)) {
646      // The last session crashed. It's possible automatically loading the
647      // page will trigger another crash, locking the user out of chrome.
648      // To avoid this, don't restore on startup but instead show the crashed
649      // infobar.
650      VLOG(1) << "Unclean exit; not processing";
651      return false;
652    }
653
654    uint32 restore_behavior = SessionRestore::SYNCHRONOUS;
655    if (browser_defaults::kAlwaysCreateTabbedBrowserOnSessionRestore ||
656        CommandLine::ForCurrentProcess()->HasSwitch(
657            switches::kCreateBrowserOnStartupForTests)) {
658      restore_behavior |= SessionRestore::ALWAYS_CREATE_TABBED_BROWSER;
659    }
660
661#if defined(OS_MACOSX)
662    // On Mac, when restoring a session with no windows, suppress the creation
663    // of a new window in the case where the system is launching Chrome via a
664    // login item or Lion's resume feature.
665    if (base::mac::WasLaunchedAsLoginOrResumeItem()) {
666      restore_behavior = restore_behavior &
667                         ~SessionRestore::ALWAYS_CREATE_TABBED_BROWSER;
668    }
669#endif
670
671    // Pause the StartupTimer. Since the restore here is synchronous, we can
672    // keep these two metrics (browser startup time and session restore time)
673    // separate.
674    performance_monitor::StartupTimer::PauseTimer();
675
676    // The startup code only executes for browsers launched in desktop mode.
677    // i.e. HOST_DESKTOP_TYPE_NATIVE. Ash should never get here.
678    Browser* browser = SessionRestore::RestoreSession(
679        profile_, NULL, desktop_type, restore_behavior,
680        urls_to_open);
681
682    performance_monitor::StartupTimer::UnpauseTimer();
683
684    AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP);
685    return true;
686  }
687
688  Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type);
689  if (!browser)
690    return false;
691
692  AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP);
693
694  // Session restore may occur if the startup preference is "last" or if the
695  // crash infobar is displayed. Otherwise, it's safe for the DOM storage system
696  // to start deleting leftover data.
697  if (pref.type != SessionStartupPref::LAST &&
698      !HasPendingUncleanExit(profile_)) {
699    content::BrowserContext::GetDefaultStoragePartition(profile_)->
700        GetDOMStorageContext()->StartScavengingUnusedSessionStorage();
701  }
702
703  return true;
704}
705
706Browser* StartupBrowserCreatorImpl::ProcessSpecifiedURLs(
707    const std::vector<GURL>& urls_to_open,
708    chrome::HostDesktopType desktop_type) {
709  SessionStartupPref pref =
710      StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_);
711  StartupTabs tabs;
712  // Pinned tabs should not be displayed when chrome is launched in incognito
713  // mode. Also, no pages should be opened automatically if the session
714  // crashed. Otherwise it might trigger another crash, locking the user out of
715  // chrome. The crash infobar is shown in this case.
716  if (!IncognitoModePrefs::ShouldLaunchIncognito(command_line_,
717                                                 profile_->GetPrefs()) &&
718      !HasPendingUncleanExit(profile_)) {
719    tabs = PinnedTabCodec::ReadPinnedTabs(profile_);
720  }
721
722  RecordAppLaunches(profile_, urls_to_open, tabs);
723
724  if (!urls_to_open.empty()) {
725    // If urls were specified on the command line, use them.
726    UrlsToTabs(urls_to_open, &tabs);
727  } else if (pref.type == SessionStartupPref::DEFAULT ||
728             (is_first_run_ &&
729              browser_creator_ && !browser_creator_->first_run_tabs_.empty())) {
730    std::vector<GURL> urls;
731    AddStartupURLs(&urls);
732    UrlsToTabs(urls, &tabs);
733  } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty() &&
734             !HasPendingUncleanExit(profile_)) {
735    // Only use the set of urls specified in preferences if nothing was
736    // specified on the command line. Filter out any urls that are to be
737    // restored by virtue of having been previously pinned.
738    AddUniqueURLs(pref.urls, &tabs);
739  } else if (pref.type == SessionStartupPref::HOMEPAGE) {
740    // If 'homepage' selected, either by the user or by a policy, we should
741    // have migrated them to another value.
742    NOTREACHED() << "SessionStartupPref has deprecated type HOMEPAGE";
743  }
744
745  if (tabs.empty())
746    return NULL;
747
748  Browser* browser = OpenTabsInBrowser(NULL, true, tabs, desktop_type);
749  return browser;
750}
751
752void StartupBrowserCreatorImpl::AddUniqueURLs(const std::vector<GURL>& urls,
753                                              StartupTabs* tabs) {
754  size_t num_existing_tabs = tabs->size();
755  for (size_t i = 0; i < urls.size(); ++i) {
756    bool in_tabs = false;
757    for (size_t j = 0; j < num_existing_tabs; ++j) {
758      if (urls[i] == (*tabs)[j].url) {
759        in_tabs = true;
760        break;
761      }
762    }
763    if (!in_tabs) {
764      StartupTab tab;
765      tab.is_pinned = false;
766      tab.url = urls[i];
767      tabs->push_back(tab);
768    }
769  }
770}
771
772Browser* StartupBrowserCreatorImpl::OpenURLsInBrowser(
773    Browser* browser,
774    bool process_startup,
775    const std::vector<GURL>& urls,
776    chrome::HostDesktopType desktop_type) {
777  StartupTabs tabs;
778  UrlsToTabs(urls, &tabs);
779  return OpenTabsInBrowser(browser, process_startup, tabs, desktop_type);
780}
781
782Browser* StartupBrowserCreatorImpl::OpenTabsInBrowser(
783    Browser* browser,
784    bool process_startup,
785    const StartupTabs& tabs,
786    chrome::HostDesktopType desktop_type) {
787  DCHECK(!tabs.empty());
788
789  // If we don't yet have a profile, try to use the one we're given from
790  // |browser|. While we may not end up actually using |browser| (since it
791  // could be a popup window), we can at least use the profile.
792  if (!profile_ && browser)
793    profile_ = browser->profile();
794
795  if (!browser || !browser->is_type_tabbed()) {
796    browser = new Browser(Browser::CreateParams(profile_, desktop_type));
797  } else {
798#if defined(TOOLKIT_GTK)
799    // Setting the time of the last action on the window here allows us to steal
800    // focus, which is what the user wants when opening a new tab in an existing
801    // browser window.
802    gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeWindow());
803#endif
804  }
805
806  // In kiosk mode, we want to always be fullscreen, so switch to that now.
807  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
808    chrome::ToggleFullscreenMode(browser);
809
810  bool first_tab = true;
811  ProtocolHandlerRegistry* registry = profile_ ?
812      ProtocolHandlerRegistryFactory::GetForProfile(profile_) : NULL;
813  for (size_t i = 0; i < tabs.size(); ++i) {
814    // We skip URLs that we'd have to launch an external protocol handler for.
815    // This avoids us getting into an infinite loop asking ourselves to open
816    // a URL, should the handler be (incorrectly) configured to be us. Anyone
817    // asking us to open such a URL should really ask the handler directly.
818    bool handled_by_chrome = ProfileIOData::IsHandledURL(tabs[i].url) ||
819        (registry && registry->IsHandledProtocol(tabs[i].url.scheme()));
820    if (!process_startup && !handled_by_chrome)
821      continue;
822
823    int add_types = first_tab ? TabStripModel::ADD_ACTIVE :
824                                TabStripModel::ADD_NONE;
825    add_types |= TabStripModel::ADD_FORCE_INDEX;
826    if (tabs[i].is_pinned)
827      add_types |= TabStripModel::ADD_PINNED;
828
829    chrome::NavigateParams params(browser, tabs[i].url,
830                                  content::PAGE_TRANSITION_AUTO_TOPLEVEL);
831    params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
832    params.tabstrip_add_types = add_types;
833    params.extension_app_id = tabs[i].app_id;
834
835#if defined(ENABLE_RLZ)
836    if (process_startup && google_util::IsGoogleHomePageUrl(tabs[i].url)) {
837      params.extra_headers = RLZTracker::GetAccessPointHttpHeader(
838          RLZTracker::CHROME_HOME_PAGE);
839    }
840#endif
841
842    chrome::Navigate(&params);
843
844    first_tab = false;
845  }
846  if (!browser->tab_strip_model()->GetActiveWebContents()) {
847    // TODO: this is a work around for 110909. Figure out why it's needed.
848    if (!browser->tab_strip_model()->count())
849      chrome::AddBlankTabAt(browser, -1, true);
850    else
851      browser->tab_strip_model()->ActivateTabAt(0, false);
852  }
853
854  // The default behavior is to show the window, as expressed by the default
855  // value of StartupBrowserCreated::show_main_browser_window_. If this was set
856  // to true ahead of this place, it means another task must have been spawned
857  // to take care of that.
858  if (!browser_creator_ || browser_creator_->show_main_browser_window())
859    browser->window()->Show();
860
861  return browser;
862}
863
864void StartupBrowserCreatorImpl::AddInfoBarsIfNecessary(
865    Browser* browser,
866    chrome::startup::IsProcessStartup is_process_startup) {
867  if (!browser || !profile_ || browser->tab_strip_model()->count() == 0)
868    return;
869
870  if (HasPendingUncleanExit(browser->profile()))
871    SessionCrashedInfoBarDelegate::Create(browser);
872
873  // The below info bars are only added to the first profile which is launched.
874  // Other profiles might be restoring the browsing sessions asynchronously,
875  // so we cannot add the info bars to the focused tabs here.
876  if (is_process_startup == chrome::startup::IS_PROCESS_STARTUP) {
877    chrome::ShowBadFlagsPrompt(browser);
878    if (!command_line_.HasSwitch(switches::kTestType)) {
879      GoogleApiKeysInfoBarDelegate::Create(InfoBarService::FromWebContents(
880          browser->tab_strip_model()->GetActiveWebContents()));
881    }
882
883    ObsoleteOSInfoBarDelegate::Create(InfoBarService::FromWebContents(
884        browser->tab_strip_model()->GetActiveWebContents()));
885
886    if (browser_defaults::kOSSupportsOtherBrowsers &&
887        !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) {
888      // Generally, the default browser prompt should not be shown on first
889      // run. However, when the set-as-default dialog has been suppressed, we
890      // need to allow it.
891      if ((!is_first_run_ ||
892           (browser_creator_ &&
893            browser_creator_->is_default_browser_dialog_suppressed())) &&
894          !chrome::ShowAutolaunchPrompt(browser)) {
895        chrome::ShowDefaultBrowserPrompt(profile_,
896                                         browser->host_desktop_type());
897      }
898    }
899  }
900}
901
902void StartupBrowserCreatorImpl::AddStartupURLs(
903    std::vector<GURL>* startup_urls) const {
904  // TODO(atwilson): Simplify the logic that decides which tabs to open on
905  // start-up and make it more consistent. http://crbug.com/248883
906
907  // If we have urls specified by the first run master preferences use them
908  // and nothing else.
909  if (browser_creator_ && startup_urls->empty()) {
910    if (!browser_creator_->first_run_tabs_.empty()) {
911      std::vector<GURL>::iterator it =
912          browser_creator_->first_run_tabs_.begin();
913      while (it != browser_creator_->first_run_tabs_.end()) {
914        // Replace magic names for the actual urls.
915        if (it->host() == "new_tab_page") {
916          startup_urls->push_back(GURL(chrome::kChromeUINewTabURL));
917        } else if (it->host() == "welcome_page") {
918          startup_urls->push_back(internals::GetWelcomePageURL());
919        } else {
920          startup_urls->push_back(*it);
921        }
922        ++it;
923      }
924      browser_creator_->first_run_tabs_.clear();
925    }
926  }
927
928  // Otherwise open at least the new tab page (and the welcome page, if this
929  // is the first time the browser is being started), or the set of URLs
930  // specified on the command line.
931  if (startup_urls->empty()) {
932    startup_urls->push_back(GURL(chrome::kChromeUINewTabURL));
933    if (first_run::ShouldShowWelcomePage())
934      startup_urls->push_back(internals::GetWelcomePageURL());
935  }
936
937  if (signin::ShouldShowPromoAtStartup(profile_, is_first_run_)) {
938    signin::DidShowPromoAtStartup(profile_);
939
940    const GURL sync_promo_url = signin::GetPromoURL(signin::SOURCE_START_PAGE,
941                                                    false);
942
943    // No need to add if the sync promo is already in the startup list.
944    bool add_promo = true;
945    for (std::vector<GURL>::const_iterator it = startup_urls->begin();
946         it != startup_urls->end(); ++it) {
947      if (*it == sync_promo_url) {
948        add_promo = false;
949        break;
950      }
951    }
952
953    if (add_promo) {
954      // If the first URL is the NTP, replace it with the sync promo. This
955      // behavior is desired because completing or skipping the sync promo
956      // causes a redirect to the NTP.
957      if (!startup_urls->empty() && IsNewTabURL(profile_, startup_urls->at(0)))
958        startup_urls->at(0) = sync_promo_url;
959      else
960        startup_urls->insert(startup_urls->begin(), sync_promo_url);
961    }
962  }
963}
964
965#if !defined(OS_WIN)
966// static
967bool StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser(
968    Profile* profile,
969    const std::vector<GURL>& startup_urls) {
970  return false;
971}
972#endif
973