browser_init.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/browser_init.h"
6
7#include <algorithm>   // For max().
8
9#include "app/l10n_util.h"
10#include "app/resource_bundle.h"
11#include "base/environment.h"
12#include "base/event_recorder.h"
13#include "base/file_path.h"
14#include "base/metrics/histogram.h"
15#include "base/path_service.h"
16#include "base/scoped_ptr.h"
17#include "base/string_number_conversions.h"
18#include "base/string_split.h"
19#include "base/thread_restrictions.h"
20#include "base/utf_string_conversions.h"
21#include "chrome/browser/automation/automation_provider.h"
22#include "chrome/browser/automation/automation_provider_list.h"
23#include "chrome/browser/automation/chrome_frame_automation_provider.h"
24#include "chrome/browser/automation/testing_automation_provider.h"
25#include "chrome/browser/browser_list.h"
26#include "chrome/browser/browser_process.h"
27#include "chrome/browser/browser_thread.h"
28#include "chrome/browser/browser_window.h"
29#include "chrome/browser/child_process_security_policy.h"
30#include "chrome/browser/defaults.h"
31#include "chrome/browser/extensions/extension_creator.h"
32#include "chrome/browser/extensions/extension_service.h"
33#include "chrome/browser/extensions/pack_extension_job.h"
34#include "chrome/browser/first_run/first_run.h"
35#include "chrome/browser/net/predictor_api.h"
36#include "chrome/browser/net/url_fixer_upper.h"
37#include "chrome/browser/notifications/desktop_notification_service.h"
38#include "chrome/browser/prefs/pref_service.h"
39#include "chrome/browser/prefs/session_startup_pref.h"
40#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
41#include "chrome/browser/profiles/profile.h"
42#include "chrome/browser/renderer_host/render_process_host.h"
43#include "chrome/browser/search_engines/template_url.h"
44#include "chrome/browser/search_engines/template_url_model.h"
45#include "chrome/browser/sessions/session_restore.h"
46#include "chrome/browser/sessions/session_service.h"
47#include "chrome/browser/shell_integration.h"
48#include "chrome/browser/tab_contents/infobar_delegate.h"
49#include "chrome/browser/tab_contents/navigation_controller.h"
50#include "chrome/browser/tab_contents/tab_contents.h"
51#include "chrome/browser/tab_contents/tab_contents_view.h"
52#include "chrome/browser/tabs/pinned_tab_codec.h"
53#include "chrome/browser/tabs/tab_strip_model.h"
54#include "chrome/browser/ui/browser_navigator.h"
55#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
56#include "chrome/common/chrome_constants.h"
57#include "chrome/common/chrome_paths.h"
58#include "chrome/common/chrome_switches.h"
59#include "chrome/common/pref_names.h"
60#include "chrome/common/result_codes.h"
61#include "chrome/common/url_constants.h"
62#include "chrome/installer/util/browser_distribution.h"
63#include "grit/chromium_strings.h"
64#include "grit/generated_resources.h"
65#include "grit/locale_settings.h"
66#include "grit/theme_resources.h"
67#include "net/base/net_util.h"
68#include "net/url_request/url_request.h"
69#include "webkit/glue/webkit_glue.h"
70
71#if defined(OS_MACOSX)
72#include "chrome/browser/ui/cocoa/keystone_infobar.h"
73#endif
74
75#if defined(OS_WIN)
76#include "app/win_util.h"
77#endif
78
79#if defined(TOOLKIT_GTK)
80#include "chrome/browser/gtk/gtk_util.h"
81#endif
82
83#if defined(OS_CHROMEOS)
84#include "chrome/browser/chromeos/brightness_observer.h"
85#include "chrome/browser/chromeos/cros/cros_library.h"
86#include "chrome/browser/chromeos/cros/mount_library.h"
87#include "chrome/browser/chromeos/cros/network_library.h"
88#include "chrome/browser/chromeos/customization_document.h"
89#include "chrome/browser/chromeos/enterprise_extension_observer.h"
90#include "chrome/browser/chromeos/gview_request_interceptor.h"
91#include "chrome/browser/chromeos/low_battery_observer.h"
92#include "chrome/browser/chromeos/network_message_observer.h"
93#include "chrome/browser/chromeos/network_state_notifier.h"
94#include "chrome/browser/chromeos/system_key_event_listener.h"
95#include "chrome/browser/chromeos/update_observer.h"
96#include "chrome/browser/chromeos/usb_mount_observer.h"
97#include "chrome/browser/chromeos/wm_message_listener.h"
98#include "chrome/browser/chromeos/wm_overview_controller.h"
99#include "chrome/browser/dom_ui/mediaplayer_ui.h"
100#endif
101
102#if defined(HAVE_XINPUT2)
103#include "views/focus/accelerator_handler.h"
104#endif
105
106namespace {
107
108class SetAsDefaultBrowserTask : public Task {
109 public:
110  SetAsDefaultBrowserTask() { }
111  virtual void Run() {
112    ShellIntegration::SetAsDefaultBrowser();
113  }
114
115 private:
116  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserTask);
117};
118
119// The delegate for the infobar shown when Chrome is not the default browser.
120class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate {
121 public:
122  explicit DefaultBrowserInfoBarDelegate(TabContents* contents)
123      : ConfirmInfoBarDelegate(contents),
124        profile_(contents->profile()),
125        action_taken_(false),
126        should_expire_(false),
127        ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
128    // We want the info-bar to stick-around for few seconds and then be hidden
129    // on the next navigation after that.
130    MessageLoop::current()->PostDelayedTask(FROM_HERE,
131        method_factory_.NewRunnableMethod(
132            &DefaultBrowserInfoBarDelegate::Expire),
133        8000);  // 8 seconds.
134  }
135
136  virtual bool ShouldExpire(
137      const NavigationController::LoadCommittedDetails& details) const {
138    return should_expire_;
139  }
140
141  // Overridden from ConfirmInfoBarDelegate:
142  virtual void InfoBarClosed() {
143    if (!action_taken_)
144      UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1);
145    delete this;
146  }
147
148  virtual string16 GetMessageText() const {
149    return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT);
150  }
151
152  virtual SkBitmap* GetIcon() const {
153    return ResourceBundle::GetSharedInstance().GetBitmapNamed(
154       IDR_PRODUCT_ICON_32);
155  }
156
157  virtual int GetButtons() const {
158    return BUTTON_OK | BUTTON_CANCEL | BUTTON_OK_DEFAULT;
159  }
160
161  virtual string16 GetButtonLabel(InfoBarButton button) const {
162    return button == BUTTON_OK ?
163        l10n_util::GetStringUTF16(IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL) :
164        l10n_util::GetStringUTF16(IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL);
165  }
166
167  virtual bool NeedElevation(InfoBarButton button) const {
168    return button == BUTTON_OK;
169  }
170
171  virtual bool Accept() {
172    action_taken_ = true;
173    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1);
174    g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
175        new SetAsDefaultBrowserTask());
176    return true;
177  }
178
179  virtual bool Cancel() {
180    action_taken_ = true;
181    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1);
182    // User clicked "Don't ask me again", remember that.
183    profile_->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, false);
184    return true;
185  }
186
187  void Expire() {
188    should_expire_ = true;
189  }
190
191 private:
192  // The Profile that we restore sessions from.
193  Profile* profile_;
194
195  // Whether the user clicked one of the buttons.
196  bool action_taken_;
197
198  // Whether the info-bar should be dismissed on the next navigation.
199  bool should_expire_;
200
201  // Used to delay the expiration of the info-bar.
202  ScopedRunnableMethodFactory<DefaultBrowserInfoBarDelegate> method_factory_;
203
204  DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate);
205};
206
207class NotifyNotDefaultBrowserTask : public Task {
208 public:
209  NotifyNotDefaultBrowserTask() { }
210
211  virtual void Run() {
212    Browser* browser = BrowserList::GetLastActive();
213    if (!browser) {
214      // Reached during ui tests.
215      return;
216    }
217    TabContents* tab = browser->GetSelectedTabContents();
218    // Don't show the info-bar if there are already info-bars showing.
219    // In ChromeBot tests, there might be a race. This line appears to get
220    // called during shutdown and |tab| can be NULL.
221    if (!tab || tab->infobar_delegate_count() > 0)
222      return;
223    tab->AddInfoBar(new DefaultBrowserInfoBarDelegate(tab));
224  }
225
226 private:
227  DISALLOW_COPY_AND_ASSIGN(NotifyNotDefaultBrowserTask);
228};
229
230class CheckDefaultBrowserTask : public Task {
231 public:
232  CheckDefaultBrowserTask() {
233  }
234
235  virtual void Run() {
236    if (ShellIntegration::IsDefaultBrowser())
237      return;
238#if defined(OS_WIN)
239    if (!BrowserDistribution::GetDistribution()->CanSetAsDefault())
240      return;
241#endif
242
243    BrowserThread::PostTask(
244        BrowserThread::UI, FROM_HERE, new NotifyNotDefaultBrowserTask());
245  }
246
247 private:
248  DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserTask);
249};
250
251// A delegate for the InfoBar shown when the previous session has crashed. The
252// bar deletes itself automatically after it is closed.
253class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate {
254 public:
255  explicit SessionCrashedInfoBarDelegate(TabContents* contents)
256      : ConfirmInfoBarDelegate(contents),
257        profile_(contents->profile()) {
258  }
259
260  // Overridden from ConfirmInfoBarDelegate:
261  virtual void InfoBarClosed() {
262    delete this;
263  }
264  virtual string16 GetMessageText() const {
265    return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE);
266  }
267  virtual SkBitmap* GetIcon() const {
268    return ResourceBundle::GetSharedInstance().GetBitmapNamed(
269        IDR_INFOBAR_RESTORE_SESSION);
270  }
271  virtual int GetButtons() const { return BUTTON_OK; }
272  virtual string16 GetButtonLabel(InfoBarButton button) const {
273    return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON);
274  }
275  virtual bool Accept() {
276    // Restore the session.
277    SessionRestore::RestoreSession(profile_, NULL, true, false,
278                                   std::vector<GURL>());
279    return true;
280  }
281
282 private:
283  // The Profile that we restore sessions from.
284  Profile* profile_;
285
286  DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate);
287};
288
289SessionStartupPref GetSessionStartupPref(const CommandLine& command_line,
290                                         Profile* profile) {
291  SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile);
292  if (command_line.HasSwitch(switches::kRestoreLastSession))
293    pref.type = SessionStartupPref::LAST;
294  if (command_line.HasSwitch(switches::kIncognito) &&
295      pref.type == SessionStartupPref::LAST) {
296    // We don't store session information when incognito. If the user has
297    // chosen to restore last session and launched incognito, fallback to
298    // default launch behavior.
299    pref.type = SessionStartupPref::DEFAULT;
300  }
301  return pref;
302}
303
304enum LaunchMode {
305  LM_TO_BE_DECIDED = 0,       // Possibly direct launch or via a shortcut.
306  LM_AS_WEBAPP,               // Launched as a installed web application.
307  LM_WITH_URLS,               // Launched with urls in the cmd line.
308  LM_SHORTCUT_NONE,           // Not launched from a shortcut.
309  LM_SHORTCUT_NONAME,         // Launched from shortcut but no name available.
310  LM_SHORTCUT_UNKNOWN,        // Launched from user-defined shortcut.
311  LM_SHORTCUT_QUICKLAUNCH,    // Launched from the quick launch bar.
312  LM_SHORTCUT_DESKTOP,        // Launched from a desktop shortcut.
313  LM_SHORTCUT_STARTMENU,      // Launched from start menu.
314  LM_LINUX_MAC_BEOS           // Other OS buckets start here.
315};
316
317#if defined(OS_WIN)
318// Undocumented flag in the startup info structure tells us what shortcut was
319// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for
320// more information. Confirmed to work on XP, Vista and Win7.
321LaunchMode GetLaunchShortcutKind() {
322  STARTUPINFOW si = { sizeof(si) };
323  GetStartupInfoW(&si);
324  if (si.dwFlags & 0x800) {
325    if (!si.lpTitle)
326      return LM_SHORTCUT_NONAME;
327    std::wstring shortcut(si.lpTitle);
328    // The windows quick launch path is not localized.
329    if (shortcut.find(L"\\Quick Launch\\") != std::wstring::npos)
330      return LM_SHORTCUT_QUICKLAUNCH;
331    scoped_ptr<base::Environment> env(base::Environment::Create());
332    std::string appdata_path;
333    env->GetVar("USERPROFILE", &appdata_path);
334    if (!appdata_path.empty() &&
335        shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos)
336      return LM_SHORTCUT_DESKTOP;
337    return LM_SHORTCUT_UNKNOWN;
338  }
339  return LM_SHORTCUT_NONE;
340}
341#else
342// TODO(cpu): Port to other platforms.
343LaunchMode GetLaunchShortcutKind() {
344  return LM_LINUX_MAC_BEOS;
345}
346#endif
347
348// Log in a histogram the frequency of launching by the different methods. See
349// LaunchMode enum for the actual values of the buckets.
350void RecordLaunchModeHistogram(LaunchMode mode) {
351  int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode;
352  UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket);
353}
354
355static bool in_startup = false;
356
357GURL GetWelcomePageURL() {
358  std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL);
359  return GURL(welcome_url);
360}
361
362void UrlsToTabs(const std::vector<GURL>& urls,
363                std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) {
364  for (size_t i = 0; i < urls.size(); ++i) {
365    BrowserInit::LaunchWithProfile::Tab tab;
366    tab.is_pinned = false;
367    tab.url = urls[i];
368    tabs->push_back(tab);
369  }
370}
371
372}  // namespace
373
374BrowserInit::BrowserInit() {}
375
376BrowserInit::~BrowserInit() {}
377
378void BrowserInit::AddFirstRunTab(const GURL& url) {
379  first_run_tabs_.push_back(url);
380}
381
382// static
383bool BrowserInit::InProcessStartup() {
384  return in_startup;
385}
386
387bool BrowserInit::LaunchBrowser(const CommandLine& command_line,
388                                Profile* profile,
389                                const FilePath& cur_dir,
390                                bool process_startup,
391                                int* return_code) {
392  in_startup = process_startup;
393  DCHECK(profile);
394#if defined(OS_CHROMEOS)
395  if (process_startup) {
396    // NetworkStateNotifier has to be initialized before Launching browser
397    // because the page load can happen in parallel to this UI thread
398    // and IO thread may access the NetworkStateNotifier.
399    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
400        ->AddNetworkManagerObserver(
401            chromeos::NetworkStateNotifier::GetInstance());
402  }
403#endif
404
405  // Continue with the off-the-record profile from here on if --incognito
406  if (command_line.HasSwitch(switches::kIncognito))
407    profile = profile->GetOffTheRecordProfile();
408
409  BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this);
410  bool launched = lwp.Launch(profile, process_startup);
411  in_startup = false;
412
413  if (!launched) {
414    LOG(ERROR) << "launch error";
415    if (return_code)
416      *return_code = ResultCodes::INVALID_CMDLINE_URL;
417    return false;
418  }
419
420#if defined(OS_CHROMEOS)
421  // Create the WmMessageListener so that it can listen for messages regardless
422  // of what window has focus.
423  chromeos::WmMessageListener::GetInstance();
424
425  // Create the SystemKeyEventListener so it can listen for system keyboard
426  // messages regardless of focus.
427  chromeos::SystemKeyEventListener::GetInstance();
428
429  // Create the WmOverviewController so it can register with the listener.
430  chromeos::WmOverviewController::GetInstance();
431
432  // Install the GView request interceptor that will redirect requests
433  // of compatible documents (PDF, etc) to the GView document viewer.
434  const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
435  if (parsed_command_line.HasSwitch(switches::kEnableGView)) {
436    chromeos::GViewRequestInterceptor::GetInstance();
437  }
438  if (process_startup) {
439    // TODO(dhg): Try to make this just USBMountObserver::Get()->set_profile
440    // and have the constructor take care of everything else.
441    chromeos::MountLibrary* lib =
442        chromeos::CrosLibrary::Get()->GetMountLibrary();
443    chromeos::USBMountObserver* observe =
444        chromeos::USBMountObserver::GetInstance();
445    lib->AddObserver(observe);
446    observe->ScanForDevices(lib);
447    // Connect the chromeos notifications
448
449    // This observer is a singleton. It is never deleted but the pointer is kept
450    // in a static so that it isn't reported as a leak.
451    static chromeos::LowBatteryObserver* low_battery_observer =
452        new chromeos::LowBatteryObserver(profile);
453    chromeos::CrosLibrary::Get()->GetPowerLibrary()->AddObserver(
454        low_battery_observer);
455
456    static chromeos::UpdateObserver* update_observer =
457        new chromeos::UpdateObserver(profile);
458    chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver(
459        update_observer);
460
461    static chromeos::NetworkMessageObserver* network_message_observer =
462        new chromeos::NetworkMessageObserver(profile);
463    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
464        ->AddNetworkManagerObserver(network_message_observer);
465    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
466        ->AddCellularDataPlanObserver(network_message_observer);
467
468    static chromeos::BrightnessObserver* brightness_observer =
469        new chromeos::BrightnessObserver();
470    chromeos::CrosLibrary::Get()->GetBrightnessLibrary()
471        ->AddObserver(brightness_observer);
472
473    profile->SetupChromeOSEnterpriseExtensionObserver();
474  }
475#endif
476  return true;
477}
478
479// Tab ------------------------------------------------------------------------
480
481BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {}
482
483BrowserInit::LaunchWithProfile::Tab::~Tab() {}
484
485// LaunchWithProfile ----------------------------------------------------------
486
487BrowserInit::LaunchWithProfile::LaunchWithProfile(
488    const FilePath& cur_dir,
489    const CommandLine& command_line)
490        : cur_dir_(cur_dir),
491          command_line_(command_line),
492          profile_(NULL),
493          browser_init_(NULL) {
494}
495
496BrowserInit::LaunchWithProfile::LaunchWithProfile(
497    const FilePath& cur_dir,
498    const CommandLine& command_line,
499    BrowserInit* browser_init)
500        : cur_dir_(cur_dir),
501          command_line_(command_line),
502          profile_(NULL),
503          browser_init_(browser_init) {
504}
505
506BrowserInit::LaunchWithProfile::~LaunchWithProfile() {
507}
508
509bool BrowserInit::LaunchWithProfile::Launch(Profile* profile,
510                                            bool process_startup) {
511  DCHECK(profile);
512  profile_ = profile;
513
514  if (command_line_.HasSwitch(switches::kDnsLogDetails))
515    chrome_browser_net::EnablePredictorDetailedLog(true);
516  if (command_line_.HasSwitch(switches::kDnsPrefetchDisable))
517    chrome_browser_net::EnablePredictor(false);
518
519  if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit))
520    base::StatisticsRecorder::set_dump_on_exit(true);
521
522  if (command_line_.HasSwitch(switches::kRemoteShellPort)) {
523    std::string port_str =
524        command_line_.GetSwitchValueASCII(switches::kRemoteShellPort);
525    int64 port;
526    if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535)
527      g_browser_process->InitDebuggerWrapper(static_cast<int>(port), false);
528    else
529      DLOG(WARNING) << "Invalid remote shell port number " << port;
530  } else if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) {
531    std::string port_str =
532        command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
533    int64 port;
534    if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535)
535      g_browser_process->InitDebuggerWrapper(static_cast<int>(port), true);
536    else
537      DLOG(WARNING) << "Invalid http debugger port number " << port;
538  }
539
540  if (command_line_.HasSwitch(switches::kUserAgent)) {
541    webkit_glue::SetUserAgent(command_line_.GetSwitchValueASCII(
542        switches::kUserAgent));
543  }
544
545  // Open the required browser windows and tabs.
546  // First, see if we're being run as an application window.
547  if (!OpenApplicationWindow(profile)) {
548    std::vector<GURL> urls_to_open = GetURLsFromCommandLine(profile_);
549    RecordLaunchModeHistogram(urls_to_open.empty()?
550                              LM_TO_BE_DECIDED : LM_WITH_URLS);
551    ProcessLaunchURLs(process_startup, urls_to_open);
552
553    // If this is an app launch, but we didn't open an app window, it may
554    // be an app tab.
555    std::string app_id;
556    if (IsAppLaunch(NULL, &app_id) && !app_id.empty()) {
557      // TODO(erikkay): This could fail if |app_id| is invalid (the app was
558      // uninstalled).  We may want to show some reasonable error here.
559      Browser::OpenApplication(profile, app_id, NULL);
560    }
561
562    if (process_startup) {
563      if (browser_defaults::kOSSupportsOtherBrowsers &&
564          !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) {
565        // Check whether we are the default browser.
566        CheckDefaultBrowser(profile);
567      }
568#if defined(OS_MACOSX)
569      // Check whether the auto-update system needs to be promoted from user
570      // to system.
571      KeystoneInfoBar::PromotionInfoBar(profile);
572#endif
573    }
574  } else {
575    RecordLaunchModeHistogram(LM_AS_WEBAPP);
576  }
577
578#if defined(OS_WIN)
579  // Print the selected page if the command line switch exists. Note that the
580  // current selected tab would be the page which will be printed.
581  if (command_line_.HasSwitch(switches::kPrint)) {
582    Browser* browser = BrowserList::GetLastActive();
583    browser->Print();
584  }
585#endif
586
587  // If we're recording or playing back, startup the EventRecorder now
588  // unless otherwise specified.
589  if (!command_line_.HasSwitch(switches::kNoEvents)) {
590    FilePath script_path;
591    PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path);
592
593    bool record_mode = command_line_.HasSwitch(switches::kRecordMode);
594    bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode);
595
596    if (record_mode && chrome::kRecordModeEnabled)
597      base::EventRecorder::current()->StartRecording(script_path);
598    if (playback_mode)
599      base::EventRecorder::current()->StartPlayback(script_path);
600  }
601
602#if defined(OS_WIN)
603  if (process_startup)
604    ShellIntegration::MigrateChromiumShortcuts();
605#endif  // defined(OS_WIN)
606
607  return true;
608}
609
610bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url,
611                                                 std::string* app_id) {
612  if (command_line_.HasSwitch(switches::kApp)) {
613    if (app_url)
614      *app_url = command_line_.GetSwitchValueASCII(switches::kApp);
615    return true;
616  }
617  if (command_line_.HasSwitch(switches::kAppId)) {
618    if (app_id)
619      *app_id = command_line_.GetSwitchValueASCII(switches::kAppId);
620    return true;
621  }
622  return false;
623}
624
625bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) {
626  std::string url_string, app_id;
627  if (!IsAppLaunch(&url_string, &app_id))
628    return false;
629
630  // This can fail if the app_id is invalid.  It can also fail if the
631  // extension is external, and has not yet been installed.
632  // TODO(skerner): Do something reasonable here. Pop up a warning panel?
633  // Open an URL to the gallery page of the extension id?
634  if (!app_id.empty()) {
635    ExtensionService* extensions_service = profile->GetExtensionService();
636    const Extension* extension =
637        extensions_service->GetExtensionById(app_id, false);
638
639    // The extension with id |app_id| may have been uninstalled.
640    if (!extension)
641      return false;
642
643    // Look at preferences to find the right launch container.  If no
644    // preference is set, launch as a window.
645    extension_misc::LaunchContainer launch_container =
646        extensions_service->extension_prefs()->GetLaunchContainer(
647            extension, ExtensionPrefs::LAUNCH_WINDOW);
648
649    TabContents* app_window = Browser::OpenApplication(
650        profile, extension, launch_container, NULL);
651    return (app_window != NULL);
652  }
653
654  if (url_string.empty())
655    return false;
656
657#if defined(OS_WIN)  // Fix up Windows shortcuts.
658  ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%");
659#endif
660  GURL url(url_string);
661
662  // Restrict allowed URLs for --app switch.
663  if (!url.is_empty() && url.is_valid()) {
664    ChildProcessSecurityPolicy *policy =
665        ChildProcessSecurityPolicy::GetInstance();
666    if (policy->IsWebSafeScheme(url.scheme()) ||
667        url.SchemeIs(chrome::kFileScheme)) {
668      TabContents* app_tab = Browser::OpenAppShortcutWindow(
669          profile,
670          url,
671          true);  // Update app info.
672      return (app_tab != NULL);
673    }
674  }
675  return false;
676}
677
678void BrowserInit::LaunchWithProfile::ProcessLaunchURLs(
679    bool process_startup,
680    const std::vector<GURL>& urls_to_open) {
681  // If we're starting up in "background mode" (no open browser window) then
682  // don't open any browser windows.
683  if (process_startup && command_line_.HasSwitch(switches::kNoStartupWindow))
684    return;
685
686  if (process_startup && ProcessStartupURLs(urls_to_open)) {
687    // ProcessStartupURLs processed the urls, nothing else to do.
688    return;
689  }
690
691  if (!process_startup &&
692      (profile_->GetSessionService() &&
693       profile_->GetSessionService()->RestoreIfNecessary(urls_to_open))) {
694    // We're already running and session restore wanted to run. This can happen
695    // at various points, such as if there is only an app window running and the
696    // user double clicked the chrome icon. Return so we don't open the urls.
697    return;
698  }
699
700  // Session restore didn't occur, open the urls.
701
702  Browser* browser = NULL;
703  std::vector<GURL> adjust_urls = urls_to_open;
704  if (adjust_urls.empty())
705    AddStartupURLs(&adjust_urls);
706  else if (!command_line_.HasSwitch(switches::kOpenInNewWindow))
707    browser = BrowserList::GetLastActiveWithProfile(profile_);
708
709  OpenURLsInBrowser(browser, process_startup, adjust_urls);
710}
711
712bool BrowserInit::LaunchWithProfile::ProcessStartupURLs(
713    const std::vector<GURL>& urls_to_open) {
714  SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_);
715  if (command_line_.HasSwitch(switches::kTestingChannelID) &&
716      !command_line_.HasSwitch(switches::kRestoreLastSession) &&
717      browser_defaults::kDefaultSessionStartupType !=
718      SessionStartupPref::DEFAULT) {
719    // When we have non DEFAULT session start type, then we won't open up a
720    // fresh session. But none of the tests are written with this in mind, so
721    // we explicitly ignore it during testing.
722    return false;
723  }
724
725  if (pref.type == SessionStartupPref::LAST) {
726    if (!profile_->DidLastSessionExitCleanly() &&
727        !command_line_.HasSwitch(switches::kRestoreLastSession)) {
728      // The last session crashed. It's possible automatically loading the
729      // page will trigger another crash, locking the user out of chrome.
730      // To avoid this, don't restore on startup but instead show the crashed
731      // infobar.
732      return false;
733    }
734    SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open);
735    return true;
736  }
737
738  std::vector<Tab> tabs = PinnedTabCodec::ReadPinnedTabs(profile_);
739
740  if (!urls_to_open.empty()) {
741    // If urls were specified on the command line, use them.
742    UrlsToTabs(urls_to_open, &tabs);
743  } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty()) {
744    // Only use the set of urls specified in preferences if nothing was
745    // specified on the command line.
746    UrlsToTabs(pref.urls, &tabs);
747  }
748
749  if (tabs.empty())
750    return false;
751
752  OpenTabsInBrowser(NULL, true, tabs);
753  return true;
754}
755
756Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser(
757    Browser* browser,
758    bool process_startup,
759    const std::vector<GURL>& urls) {
760  std::vector<Tab> tabs;
761  UrlsToTabs(urls, &tabs);
762  return OpenTabsInBrowser(browser, process_startup, tabs);
763}
764
765Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser(
766        Browser* browser,
767        bool process_startup,
768        const std::vector<Tab>& tabs) {
769  DCHECK(!tabs.empty());
770  // If we don't yet have a profile, try to use the one we're given from
771  // |browser|. While we may not end up actually using |browser| (since it
772  // could be a popup window), we can at least use the profile.
773  if (!profile_ && browser)
774    profile_ = browser->profile();
775
776  if (!browser || browser->type() != Browser::TYPE_NORMAL) {
777    browser = Browser::Create(profile_);
778  } else {
779#if defined(TOOLKIT_GTK)
780    // Setting the time of the last action on the window here allows us to steal
781    // focus, which is what the user wants when opening a new tab in an existing
782    // browser window.
783    gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle());
784#endif
785  }
786
787#if !defined(OS_MACOSX)
788  // In kiosk mode, we want to always be fullscreen, so switch to that now.
789  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
790    browser->ToggleFullscreenMode();
791#endif
792
793  bool first_tab = true;
794  for (size_t i = 0; i < tabs.size(); ++i) {
795    // We skip URLs that we'd have to launch an external protocol handler for.
796    // This avoids us getting into an infinite loop asking ourselves to open
797    // a URL, should the handler be (incorrectly) configured to be us. Anyone
798    // asking us to open such a URL should really ask the handler directly.
799    if (!process_startup && !net::URLRequest::IsHandledURL(tabs[i].url))
800      continue;
801
802    int add_types = first_tab ? TabStripModel::ADD_SELECTED :
803                                TabStripModel::ADD_NONE;
804    add_types |= TabStripModel::ADD_FORCE_INDEX;
805    if (tabs[i].is_pinned)
806      add_types |= TabStripModel::ADD_PINNED;
807    int index = browser->GetIndexForInsertionDuringRestore(i);
808
809    browser::NavigateParams params(browser, tabs[i].url,
810                                   PageTransition::START_PAGE);
811    params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
812    params.tabstrip_index = index;
813    params.tabstrip_add_types = add_types;
814    params.extension_app_id = tabs[i].app_id;
815    browser::Navigate(&params);
816
817    if (profile_ && first_tab && process_startup) {
818      AddCrashedInfoBarIfNecessary(params.target_contents->tab_contents());
819      AddBadFlagsInfoBarIfNecessary(params.target_contents->tab_contents());
820    }
821
822    first_tab = false;
823  }
824  browser->window()->Show();
825  // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial
826  //                focus explicitly.
827  browser->GetSelectedTabContents()->view()->SetInitialFocus();
828
829  return browser;
830}
831
832void BrowserInit::LaunchWithProfile::AddCrashedInfoBarIfNecessary(
833    TabContents* tab) {
834  // Assume that if the user is launching incognito they were previously
835  // running incognito so that we have nothing to restore from.
836  if (!profile_->DidLastSessionExitCleanly() &&
837      !profile_->IsOffTheRecord()) {
838    // The last session didn't exit cleanly. Show an infobar to the user
839    // so that they can restore if they want. The delegate deletes itself when
840    // it is closed.
841    tab->AddInfoBar(new SessionCrashedInfoBarDelegate(tab));
842  }
843}
844
845void BrowserInit::LaunchWithProfile::AddBadFlagsInfoBarIfNecessary(
846    TabContents* tab) {
847  // Unsupported flags for which to display a warning that "stability and
848  // security will suffer".
849  static const char* kBadFlags[] = {
850    // All imply disabling the sandbox.
851    switches::kSingleProcess,
852    switches::kNoSandbox,
853    switches::kInProcessWebGL,
854    NULL
855  };
856
857  const char* bad_flag = NULL;
858  for (const char** flag = kBadFlags; *flag; ++flag) {
859    if (command_line_.HasSwitch(*flag)) {
860      bad_flag = *flag;
861      break;
862    }
863  }
864
865  if (bad_flag) {
866    tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab,
867        l10n_util::GetStringFUTF16(IDS_BAD_FLAGS_WARNING_MESSAGE,
868                                   UTF8ToUTF16(std::string("--") + bad_flag)),
869        NULL, false));
870  }
871}
872
873std::vector<GURL> BrowserInit::LaunchWithProfile::GetURLsFromCommandLine(
874    Profile* profile) {
875  std::vector<GURL> urls;
876  const std::vector<CommandLine::StringType>& params = command_line_.args();
877
878  for (size_t i = 0; i < params.size(); ++i) {
879    FilePath param = FilePath(params[i]);
880    // Handle Vista way of searching - "? <search-term>"
881    if (param.value().find(FILE_PATH_LITERAL("? ")) == 0) {
882      const TemplateURL* default_provider =
883          profile->GetTemplateURLModel()->GetDefaultSearchProvider();
884      if (!default_provider || !default_provider->url()) {
885        // No search provider available. Just treat this as regular URL.
886        urls.push_back(URLFixerUpper::FixupRelativeFile(cur_dir_, param));
887        continue;
888      }
889      const TemplateURLRef* search_url = default_provider->url();
890      DCHECK(search_url->SupportsReplacement());
891      std::wstring search_term = param.ToWStringHack().substr(2);
892      urls.push_back(GURL(search_url->ReplaceSearchTerms(
893          *default_provider, search_term,
894          TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring())));
895    } else {
896      // This will create a file URL or a regular URL.
897      // This call can (in rare circumstances) block the UI thread.
898      // Allow it until this bug is fixed.
899      //  http://code.google.com/p/chromium/issues/detail?id=60641
900      GURL url;
901      {
902        base::ThreadRestrictions::ScopedAllowIO allow_io;
903        url = URLFixerUpper::FixupRelativeFile(cur_dir_, param);
904      }
905      // Exclude dangerous schemes.
906      if (url.is_valid()) {
907        ChildProcessSecurityPolicy *policy =
908            ChildProcessSecurityPolicy::GetInstance();
909        if (policy->IsWebSafeScheme(url.scheme()) ||
910            url.SchemeIs(chrome::kFileScheme) ||
911#if defined(OS_CHROMEOS)
912            // In ChromeOS, allow a settings page to be specified on the
913            // command line. See ExistingUserController::OnLoginSuccess.
914            (url.spec().find(chrome::kChromeUISettingsURL) == 0) ||
915#endif
916            (url.spec().compare(chrome::kAboutBlankURL) == 0)) {
917          urls.push_back(url);
918        }
919      }
920    }
921  }
922  return urls;
923}
924
925void BrowserInit::LaunchWithProfile::AddStartupURLs(
926    std::vector<GURL>* startup_urls) const {
927  // If we have urls specified beforehand (i.e. from command line) use them
928  // and nothing else.
929  if (!startup_urls->empty())
930    return;
931  // If we have urls specified by the first run master preferences use them
932  // and nothing else.
933  if (browser_init_) {
934    if (!browser_init_->first_run_tabs_.empty()) {
935      std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin();
936      while (it != browser_init_->first_run_tabs_.end()) {
937        // Replace magic names for the actual urls.
938        if (it->host() == "new_tab_page") {
939          startup_urls->push_back(GURL());
940        } else if (it->host() == "welcome_page") {
941          startup_urls->push_back(GetWelcomePageURL());
942        } else {
943          startup_urls->push_back(*it);
944        }
945        ++it;
946      }
947      browser_init_->first_run_tabs_.clear();
948      return;
949    }
950  }
951
952  // Otherwise open at least the new tab page (and the welcome page, if this
953  // is the first time the browser is being started), or the set of URLs
954  // specified on the command line.
955  startup_urls->push_back(GURL());  // New tab page.
956  PrefService* prefs = g_browser_process->local_state();
957  if (prefs->FindPreference(prefs::kShouldShowWelcomePage) &&
958      prefs->GetBoolean(prefs::kShouldShowWelcomePage)) {
959    // Reset the preference so we don't show the welcome page next time.
960    prefs->ClearPref(prefs::kShouldShowWelcomePage);
961    startup_urls->push_back(GetWelcomePageURL());
962  }
963}
964
965void BrowserInit::LaunchWithProfile::CheckDefaultBrowser(Profile* profile) {
966  // We do not check if we are the default browser if:
967  // - the user said "don't ask me again" on the infobar earlier.
968  // - this is the first launch after the first run flow.
969  if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser) ||
970      FirstRun::IsChromeFirstRun()) {
971    return;
972  }
973  BrowserThread::PostTask(
974      BrowserThread::FILE, FROM_HERE, new CheckDefaultBrowserTask());
975}
976
977bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
978                                     const FilePath& cur_dir,
979                                     bool process_startup,
980                                     Profile* profile,
981                                     int* return_code,
982                                     BrowserInit* browser_init) {
983  DCHECK(profile);
984  if (process_startup) {
985    if (command_line.HasSwitch(switches::kDisablePromptOnRepost))
986      NavigationController::DisablePromptOnRepost();
987
988    // Look for the testing channel ID ONLY during process startup
989    if (command_line.HasSwitch(switches::kTestingChannelID)) {
990      std::string testing_channel_id = command_line.GetSwitchValueASCII(
991          switches::kTestingChannelID);
992      // TODO(sanjeevr) Check if we need to make this a singleton for
993      // compatibility with the old testing code
994      // If there are any extra parameters, we expect each one to generate a
995      // new tab; if there are none then we get one homepage tab.
996      int expected_tab_count = 1;
997      if (command_line.HasSwitch(switches::kNoStartupWindow)) {
998        expected_tab_count = 0;
999      } else if (command_line.HasSwitch(switches::kRestoreLastSession)) {
1000        std::string restore_session_value(
1001            command_line.GetSwitchValueASCII(switches::kRestoreLastSession));
1002        base::StringToInt(restore_session_value, &expected_tab_count);
1003      } else {
1004        expected_tab_count =
1005            std::max(1, static_cast<int>(command_line.args().size()));
1006      }
1007      if (!CreateAutomationProvider<TestingAutomationProvider>(
1008          testing_channel_id,
1009          profile,
1010          static_cast<size_t>(expected_tab_count)))
1011        return false;
1012    }
1013  }
1014
1015  bool silent_launch = false;
1016
1017  if (command_line.HasSwitch(switches::kAutomationClientChannelID)) {
1018    std::string automation_channel_id = command_line.GetSwitchValueASCII(
1019        switches::kAutomationClientChannelID);
1020    // If there are any extra parameters, we expect each one to generate a
1021    // new tab; if there are none then we have no tabs
1022    size_t expected_tabs =
1023        std::max(static_cast<int>(command_line.args().size()), 0);
1024    if (expected_tabs == 0)
1025      silent_launch = true;
1026
1027    if (command_line.HasSwitch(switches::kChromeFrame)) {
1028      if (!CreateAutomationProvider<ChromeFrameAutomationProvider>(
1029          automation_channel_id, profile, expected_tabs))
1030        return false;
1031    } else {
1032      if (!CreateAutomationProvider<AutomationProvider>(
1033          automation_channel_id, profile, expected_tabs))
1034        return false;
1035    }
1036  }
1037
1038  // If we have been invoked to display a desktop notification on behalf of
1039  // the service process, we do not want to open any browser windows.
1040  if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) {
1041    silent_launch = true;
1042    profile->GetCloudPrintProxyService()->ShowTokenExpiredNotification();
1043  }
1044
1045  if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) {
1046    std::string allowed_ports =
1047        command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts);
1048    net::SetExplicitlyAllowedPorts(allowed_ports);
1049  }
1050
1051#if defined(OS_CHROMEOS)
1052  // The browser will be launched after the user logs in.
1053  if (command_line.HasSwitch(switches::kLoginManager) ||
1054      command_line.HasSwitch(switches::kLoginPassword)) {
1055    silent_launch = true;
1056  }
1057#endif
1058
1059#if defined(HAVE_XINPUT2) && defined(TOUCH_UI)
1060  // Get a list of pointer-devices that should be treated as touch-devices.
1061  // TODO(sad): Instead of/in addition to getting the list from the
1062  // command-line, query X for a list of touch devices.
1063  std::string touch_devices =
1064    command_line.GetSwitchValueASCII(switches::kTouchDevices);
1065
1066  if (!touch_devices.empty()) {
1067    std::vector<std::string> devs;
1068    std::vector<unsigned int> device_ids;
1069    unsigned int devid;
1070    base::SplitString(touch_devices, ',', &devs);
1071    for (std::vector<std::string>::iterator iter = devs.begin();
1072        iter != devs.end(); ++iter) {
1073      if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) {
1074        device_ids.push_back(devid);
1075      } else {
1076        DLOG(WARNING) << "Invalid touch-device id: " << *iter;
1077      }
1078    }
1079    views::SetTouchDeviceList(device_ids);
1080  }
1081#endif
1082
1083  // If we don't want to launch a new browser window or tab (in the case
1084  // of an automation request), we are done here.
1085  if (!silent_launch) {
1086    return browser_init->LaunchBrowser(
1087        command_line, profile, cur_dir, process_startup, return_code);
1088  }
1089  return true;
1090}
1091
1092template <class AutomationProviderClass>
1093bool BrowserInit::CreateAutomationProvider(const std::string& channel_id,
1094                                           Profile* profile,
1095                                           size_t expected_tabs) {
1096  scoped_refptr<AutomationProviderClass> automation =
1097      new AutomationProviderClass(profile);
1098
1099  if (!automation->InitializeChannel(channel_id))
1100    return false;
1101  automation->SetExpectedTabCount(expected_tabs);
1102
1103  AutomationProviderList* list =
1104      g_browser_process->InitAutomationProviderList();
1105  DCHECK(list);
1106  list->AddProvider(automation);
1107
1108  return true;
1109}
1110