1// Copyright (c) 2011 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 "base/compiler_specific.h"
10#include "base/environment.h"
11#include "base/event_recorder.h"
12#include "base/file_path.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/metrics/histogram.h"
15#include "base/path_service.h"
16#include "base/string_number_conversions.h"
17#include "base/string_split.h"
18#include "base/threading/thread_restrictions.h"
19#include "base/utf_string_conversions.h"
20#include "chrome/browser/automation/automation_provider.h"
21#include "chrome/browser/automation/automation_provider_list.h"
22#include "chrome/browser/automation/chrome_frame_automation_provider.h"
23#include "chrome/browser/automation/testing_automation_provider.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/defaults.h"
26#include "chrome/browser/extensions/extension_creator.h"
27#include "chrome/browser/extensions/extension_service.h"
28#include "chrome/browser/extensions/pack_extension_job.h"
29#include "chrome/browser/first_run/first_run.h"
30#include "chrome/browser/net/predictor_api.h"
31#include "chrome/browser/net/url_fixer_upper.h"
32#include "chrome/browser/notifications/desktop_notification_service.h"
33#include "chrome/browser/platform_util.h"
34#include "chrome/browser/prefs/pref_service.h"
35#include "chrome/browser/prefs/session_startup_pref.h"
36#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
37#include "chrome/browser/printing/print_dialog_cloud.h"
38#include "chrome/browser/profiles/profile.h"
39#include "chrome/browser/search_engines/template_url.h"
40#include "chrome/browser/search_engines/template_url_model.h"
41#include "chrome/browser/sessions/session_restore.h"
42#include "chrome/browser/sessions/session_service.h"
43#include "chrome/browser/shell_integration.h"
44#include "chrome/browser/tab_contents/link_infobar_delegate.h"
45#include "chrome/browser/tab_contents/simple_alert_infobar_delegate.h"
46#include "chrome/browser/tabs/pinned_tab_codec.h"
47#include "chrome/browser/tabs/tab_strip_model.h"
48#include "chrome/browser/ui/browser_list.h"
49#include "chrome/browser/ui/browser_navigator.h"
50#include "chrome/browser/ui/browser_window.h"
51#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
52#include "chrome/common/chrome_constants.h"
53#include "chrome/common/chrome_paths.h"
54#include "chrome/common/chrome_switches.h"
55#include "chrome/common/extensions/extension_constants.h"
56#include "chrome/common/pref_names.h"
57#include "chrome/common/url_constants.h"
58#include "chrome/installer/util/browser_distribution.h"
59#include "content/browser/browser_thread.h"
60#include "content/browser/child_process_security_policy.h"
61#include "content/browser/renderer_host/render_process_host.h"
62#include "content/browser/tab_contents/navigation_controller.h"
63#include "content/browser/tab_contents/tab_contents.h"
64#include "content/browser/tab_contents/tab_contents_view.h"
65#include "content/common/result_codes.h"
66#include "grit/chromium_strings.h"
67#include "grit/generated_resources.h"
68#include "grit/locale_settings.h"
69#include "grit/theme_resources.h"
70#include "net/base/net_util.h"
71#include "net/url_request/url_request.h"
72#include "ui/base/l10n/l10n_util.h"
73#include "ui/base/resource/resource_bundle.h"
74#include "webkit/glue/webkit_glue.h"
75
76#if defined(OS_MACOSX)
77#include "chrome/browser/ui/cocoa/keystone_infobar.h"
78#endif
79
80#if defined(TOOLKIT_USES_GTK)
81#include "chrome/browser/ui/gtk/gtk_util.h"
82#endif
83
84#if defined(OS_CHROMEOS)
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/sms_observer.h"
95#include "chrome/browser/chromeos/update_observer.h"
96#include "chrome/browser/chromeos/wm_message_listener.h"
97#include "chrome/browser/chromeos/wm_overview_controller.h"
98#include "chrome/browser/ui/webui/mediaplayer_ui.h"
99#endif
100
101#if defined(HAVE_XINPUT2)
102#include "views/focus/accelerator_handler.h"
103#endif
104
105namespace {
106
107// SetAsDefaultBrowserTask ----------------------------------------------------
108
109class SetAsDefaultBrowserTask : public Task {
110 public:
111  SetAsDefaultBrowserTask();
112  virtual ~SetAsDefaultBrowserTask();
113
114 private:
115  virtual void Run();
116
117  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserTask);
118};
119
120SetAsDefaultBrowserTask::SetAsDefaultBrowserTask() {
121}
122
123SetAsDefaultBrowserTask::~SetAsDefaultBrowserTask() {
124}
125
126void SetAsDefaultBrowserTask::Run() {
127  ShellIntegration::SetAsDefaultBrowser();
128}
129
130
131// DefaultBrowserInfoBarDelegate ----------------------------------------------
132
133// The delegate for the infobar shown when Chrome is not the default browser.
134class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate {
135 public:
136  explicit DefaultBrowserInfoBarDelegate(TabContents* contents);
137
138 private:
139  virtual ~DefaultBrowserInfoBarDelegate();
140
141  void AllowExpiry() { should_expire_ = true; }
142
143  // ConfirmInfoBarDelegate:
144  virtual bool ShouldExpire(
145      const NavigationController::LoadCommittedDetails& details) const OVERRIDE;
146  virtual void InfoBarClosed() OVERRIDE;
147  virtual SkBitmap* GetIcon() const OVERRIDE;
148  virtual string16 GetMessageText() const OVERRIDE;
149  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
150  virtual bool NeedElevation(InfoBarButton button) const OVERRIDE;
151  virtual bool Accept() OVERRIDE;
152  virtual bool Cancel() OVERRIDE;
153
154  // The Profile that we restore sessions from.
155  Profile* profile_;
156
157  // Whether the user clicked one of the buttons.
158  bool action_taken_;
159
160  // Whether the info-bar should be dismissed on the next navigation.
161  bool should_expire_;
162
163  // Used to delay the expiration of the info-bar.
164  ScopedRunnableMethodFactory<DefaultBrowserInfoBarDelegate> method_factory_;
165
166  DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate);
167};
168
169DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate(
170    TabContents* contents)
171    : ConfirmInfoBarDelegate(contents),
172      profile_(contents->profile()),
173      action_taken_(false),
174      should_expire_(false),
175      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
176  // We want the info-bar to stick-around for few seconds and then be hidden
177  // on the next navigation after that.
178  MessageLoop::current()->PostDelayedTask(FROM_HERE,
179      method_factory_.NewRunnableMethod(
180          &DefaultBrowserInfoBarDelegate::AllowExpiry), 8000);  // 8 seconds.
181}
182
183DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() {
184}
185
186bool DefaultBrowserInfoBarDelegate::ShouldExpire(
187    const NavigationController::LoadCommittedDetails& details) const {
188  return should_expire_;
189}
190
191void DefaultBrowserInfoBarDelegate::InfoBarClosed() {
192  if (!action_taken_)
193    UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1);
194  delete this;
195}
196
197SkBitmap* DefaultBrowserInfoBarDelegate::GetIcon() const {
198  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
199     IDR_PRODUCT_ICON_32);
200}
201
202string16 DefaultBrowserInfoBarDelegate::GetMessageText() const {
203  return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT);
204}
205
206string16 DefaultBrowserInfoBarDelegate::GetButtonLabel(
207    InfoBarButton button) const {
208  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
209      IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL :
210      IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL);
211}
212
213bool DefaultBrowserInfoBarDelegate::NeedElevation(InfoBarButton button) const {
214  return button == BUTTON_OK;
215}
216
217bool DefaultBrowserInfoBarDelegate::Accept() {
218  action_taken_ = true;
219  UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1);
220  g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
221      new SetAsDefaultBrowserTask());
222  return true;
223}
224
225bool DefaultBrowserInfoBarDelegate::Cancel() {
226  action_taken_ = true;
227  UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1);
228  // User clicked "Don't ask me again", remember that.
229  profile_->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, false);
230  return true;
231}
232
233
234// NotifyNotDefaultBrowserTask ------------------------------------------------
235
236class NotifyNotDefaultBrowserTask : public Task {
237 public:
238  NotifyNotDefaultBrowserTask();
239  virtual ~NotifyNotDefaultBrowserTask();
240
241 private:
242  virtual void Run();
243
244  DISALLOW_COPY_AND_ASSIGN(NotifyNotDefaultBrowserTask);
245};
246
247NotifyNotDefaultBrowserTask::NotifyNotDefaultBrowserTask() {
248}
249
250NotifyNotDefaultBrowserTask::~NotifyNotDefaultBrowserTask() {
251}
252
253void NotifyNotDefaultBrowserTask::Run() {
254  Browser* browser = BrowserList::GetLastActive();
255  if (!browser)
256    return;  // Reached during ui tests.
257  // Don't show the info-bar if there are already info-bars showing.
258  // In ChromeBot tests, there might be a race. This line appears to get
259  // called during shutdown and |tab| can be NULL.
260  TabContents* tab = browser->GetSelectedTabContents();
261  if (!tab || tab->infobar_count() > 0)
262    return;
263  tab->AddInfoBar(new DefaultBrowserInfoBarDelegate(tab));
264}
265
266
267// CheckDefaultBrowserTask ----------------------------------------------------
268
269class CheckDefaultBrowserTask : public Task {
270 public:
271  CheckDefaultBrowserTask();
272  virtual ~CheckDefaultBrowserTask();
273
274 private:
275  virtual void Run();
276
277  DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserTask);
278};
279
280CheckDefaultBrowserTask::CheckDefaultBrowserTask() {
281}
282
283CheckDefaultBrowserTask::~CheckDefaultBrowserTask() {
284}
285
286void CheckDefaultBrowserTask::Run() {
287  if (ShellIntegration::IsDefaultBrowser() ||
288      !platform_util::CanSetAsDefaultBrowser()) {
289    return;
290  }
291  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
292                          new NotifyNotDefaultBrowserTask());
293}
294
295
296// SessionCrashedInfoBarDelegate ----------------------------------------------
297
298// A delegate for the InfoBar shown when the previous session has crashed.
299class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate {
300 public:
301  explicit SessionCrashedInfoBarDelegate(TabContents* contents);
302
303 private:
304  virtual ~SessionCrashedInfoBarDelegate();
305
306  // ConfirmInfoBarDelegate:
307  virtual void InfoBarClosed() OVERRIDE;
308  virtual SkBitmap* GetIcon() const OVERRIDE;
309  virtual string16 GetMessageText() const OVERRIDE;
310  virtual int GetButtons() const OVERRIDE;
311  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
312  virtual bool Accept() OVERRIDE;
313
314  // The Profile that we restore sessions from.
315  Profile* profile_;
316
317  DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate);
318};
319
320SessionCrashedInfoBarDelegate::SessionCrashedInfoBarDelegate(
321    TabContents* contents)
322    : ConfirmInfoBarDelegate(contents),
323      profile_(contents->profile()) {
324}
325
326SessionCrashedInfoBarDelegate::~SessionCrashedInfoBarDelegate() {
327}
328
329void SessionCrashedInfoBarDelegate::InfoBarClosed() {
330  delete this;
331}
332
333SkBitmap* SessionCrashedInfoBarDelegate::GetIcon() const {
334  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
335      IDR_INFOBAR_RESTORE_SESSION);
336}
337
338string16 SessionCrashedInfoBarDelegate::GetMessageText() const {
339  return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE);
340}
341
342int SessionCrashedInfoBarDelegate::GetButtons() const {
343  return BUTTON_OK;
344}
345
346string16 SessionCrashedInfoBarDelegate::GetButtonLabel(
347    InfoBarButton button) const {
348  DCHECK_EQ(BUTTON_OK, button);
349  return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON);
350}
351
352bool SessionCrashedInfoBarDelegate::Accept() {
353  SessionRestore::RestoreSession(profile_, NULL, true, false,
354                                 std::vector<GURL>());
355  return true;
356}
357
358
359// Utility functions ----------------------------------------------------------
360
361SessionStartupPref GetSessionStartupPref(const CommandLine& command_line,
362                                         Profile* profile) {
363  SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile);
364  if (command_line.HasSwitch(switches::kRestoreLastSession))
365    pref.type = SessionStartupPref::LAST;
366  if (command_line.HasSwitch(switches::kIncognito) &&
367      pref.type == SessionStartupPref::LAST &&
368      profile->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled)) {
369    // We don't store session information when incognito. If the user has
370    // chosen to restore last session and launched incognito, fallback to
371    // default launch behavior.
372    pref.type = SessionStartupPref::DEFAULT;
373  }
374  return pref;
375}
376
377enum LaunchMode {
378  LM_TO_BE_DECIDED = 0,       // Possibly direct launch or via a shortcut.
379  LM_AS_WEBAPP,               // Launched as a installed web application.
380  LM_WITH_URLS,               // Launched with urls in the cmd line.
381  LM_SHORTCUT_NONE,           // Not launched from a shortcut.
382  LM_SHORTCUT_NONAME,         // Launched from shortcut but no name available.
383  LM_SHORTCUT_UNKNOWN,        // Launched from user-defined shortcut.
384  LM_SHORTCUT_QUICKLAUNCH,    // Launched from the quick launch bar.
385  LM_SHORTCUT_DESKTOP,        // Launched from a desktop shortcut.
386  LM_SHORTCUT_STARTMENU,      // Launched from start menu.
387  LM_LINUX_MAC_BEOS           // Other OS buckets start here.
388};
389
390#if defined(OS_WIN)
391// Undocumented flag in the startup info structure tells us what shortcut was
392// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for
393// more information. Confirmed to work on XP, Vista and Win7.
394LaunchMode GetLaunchShortcutKind() {
395  STARTUPINFOW si = { sizeof(si) };
396  GetStartupInfoW(&si);
397  if (si.dwFlags & 0x800) {
398    if (!si.lpTitle)
399      return LM_SHORTCUT_NONAME;
400    std::wstring shortcut(si.lpTitle);
401    // The windows quick launch path is not localized.
402    if (shortcut.find(L"\\Quick Launch\\") != std::wstring::npos)
403      return LM_SHORTCUT_QUICKLAUNCH;
404    scoped_ptr<base::Environment> env(base::Environment::Create());
405    std::string appdata_path;
406    env->GetVar("USERPROFILE", &appdata_path);
407    if (!appdata_path.empty() &&
408        shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos)
409      return LM_SHORTCUT_DESKTOP;
410    return LM_SHORTCUT_UNKNOWN;
411  }
412  return LM_SHORTCUT_NONE;
413}
414#else
415// TODO(cpu): Port to other platforms.
416LaunchMode GetLaunchShortcutKind() {
417  return LM_LINUX_MAC_BEOS;
418}
419#endif
420
421// Log in a histogram the frequency of launching by the different methods. See
422// LaunchMode enum for the actual values of the buckets.
423void RecordLaunchModeHistogram(LaunchMode mode) {
424  int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode;
425  UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket);
426}
427
428static bool in_startup = false;
429
430GURL GetWelcomePageURL() {
431  std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL);
432  return GURL(welcome_url);
433}
434
435void UrlsToTabs(const std::vector<GURL>& urls,
436                std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) {
437  for (size_t i = 0; i < urls.size(); ++i) {
438    BrowserInit::LaunchWithProfile::Tab tab;
439    tab.is_pinned = false;
440    tab.url = urls[i];
441    tabs->push_back(tab);
442  }
443}
444
445// Return true if the command line option --app-id is used.  Set
446// |out_extension| to the app to open, and |out_launch_container|
447// to the type of window into which the app should be open.
448bool GetAppLaunchContainer(
449    Profile* profile,
450    const std::string& app_id,
451    const Extension** out_extension,
452    extension_misc::LaunchContainer* out_launch_container) {
453
454  ExtensionService* extensions_service = profile->GetExtensionService();
455  const Extension* extension =
456      extensions_service->GetExtensionById(app_id, false);
457
458  // The extension with id |app_id| may have been uninstalled.
459  if (!extension)
460    return false;
461
462  // Look at preferences to find the right launch container.  If no
463  // preference is set, launch as a window.
464  extension_misc::LaunchContainer launch_container =
465      extensions_service->extension_prefs()->GetLaunchContainer(
466          extension, ExtensionPrefs::LAUNCH_WINDOW);
467
468  *out_extension = extension;
469  *out_launch_container = launch_container;
470  return true;
471}
472
473void RecordCmdLineAppHistogram() {
474  UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
475                            extension_misc::APP_LAUNCH_CMD_LINE_APP,
476                            extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
477}
478
479void RecordAppLaunches(
480    Profile* profile,
481    const std::vector<GURL>& cmd_line_urls,
482    const std::vector<BrowserInit::LaunchWithProfile::Tab>& autolaunch_tabs) {
483  ExtensionService* extension_service = profile->GetExtensionService();
484  DCHECK(extension_service);
485  for (size_t i = 0; i < cmd_line_urls.size(); ++i) {
486    if (extension_service->IsInstalledApp(cmd_line_urls.at(i))) {
487      UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
488                                extension_misc::APP_LAUNCH_CMD_LINE_URL,
489                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
490    }
491  }
492  for (size_t i = 0; i < autolaunch_tabs.size(); ++i) {
493    if (extension_service->IsInstalledApp(autolaunch_tabs.at(i).url)) {
494      UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
495                                extension_misc::APP_LAUNCH_AUTOLAUNCH,
496                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
497    }
498  }
499}
500
501}  // namespace
502
503
504// BrowserInit ----------------------------------------------------------------
505
506BrowserInit::BrowserInit() {}
507
508BrowserInit::~BrowserInit() {}
509
510void BrowserInit::AddFirstRunTab(const GURL& url) {
511  first_run_tabs_.push_back(url);
512}
513
514// static
515bool BrowserInit::InProcessStartup() {
516  return in_startup;
517}
518
519bool BrowserInit::LaunchBrowser(const CommandLine& command_line,
520                                Profile* profile,
521                                const FilePath& cur_dir,
522                                bool process_startup,
523                                int* return_code) {
524  in_startup = process_startup;
525  DCHECK(profile);
526#if defined(OS_CHROMEOS)
527  if (process_startup) {
528    // NetworkStateNotifier has to be initialized before Launching browser
529    // because the page load can happen in parallel to this UI thread
530    // and IO thread may access the NetworkStateNotifier.
531    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
532        ->AddNetworkManagerObserver(
533            chromeos::NetworkStateNotifier::GetInstance());
534  }
535#endif
536
537  // Continue with the incognito profile from here on if --incognito
538  if (command_line.HasSwitch(switches::kIncognito) &&
539      profile->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled)) {
540    profile = profile->GetOffTheRecordProfile();
541  }
542
543  BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this);
544  std::vector<GURL> urls_to_launch = BrowserInit::GetURLsFromCommandLine(
545      command_line, cur_dir, profile);
546  bool launched = lwp.Launch(profile, urls_to_launch, process_startup);
547  in_startup = false;
548
549  if (!launched) {
550    LOG(ERROR) << "launch error";
551    if (return_code)
552      *return_code = ResultCodes::INVALID_CMDLINE_URL;
553    return false;
554  }
555
556#if defined(OS_CHROMEOS)
557  // Initialize Chrome OS preferences like touch pad sensitivity. For the
558  // preferences to work in the guest mode, the initialization has to be
559  // done after |profile| is switched to the incognito profile (which
560  // is actually GuestSessionProfile in the guest mode). See the
561  // GetOffTheRecordProfile() call above.
562  profile->InitChromeOSPreferences();
563
564  // Create the WmMessageListener so that it can listen for messages regardless
565  // of what window has focus.
566  chromeos::WmMessageListener::GetInstance();
567
568  // Create the WmOverviewController so it can register with the listener.
569  chromeos::WmOverviewController::GetInstance();
570
571  // Install the GView request interceptor that will redirect requests
572  // of compatible documents (PDF, etc) to the GView document viewer.
573  const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
574  if (parsed_command_line.HasSwitch(switches::kEnableGView)) {
575    chromeos::GViewRequestInterceptor::GetInstance();
576  }
577  if (process_startup) {
578    // This observer is a singleton. It is never deleted but the pointer is kept
579    // in a static so that it isn't reported as a leak.
580    static chromeos::LowBatteryObserver* low_battery_observer =
581        new chromeos::LowBatteryObserver(profile);
582    chromeos::CrosLibrary::Get()->GetPowerLibrary()->AddObserver(
583        low_battery_observer);
584
585    static chromeos::UpdateObserver* update_observer =
586        new chromeos::UpdateObserver(profile);
587    chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver(
588        update_observer);
589
590    static chromeos::NetworkMessageObserver* network_message_observer =
591        new chromeos::NetworkMessageObserver(profile);
592    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
593        ->AddNetworkManagerObserver(network_message_observer);
594    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
595        ->AddCellularDataPlanObserver(network_message_observer);
596    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
597        ->AddUserActionObserver(network_message_observer);
598
599    static chromeos::SmsObserver* sms_observer =
600        new chromeos::SmsObserver(profile);
601    chromeos::CrosLibrary::Get()->GetNetworkLibrary()
602        ->AddNetworkManagerObserver(sms_observer);
603
604    profile->SetupChromeOSEnterpriseExtensionObserver();
605  }
606#endif
607  return true;
608}
609
610
611// BrowserInit::LaunchWithProfile::Tab ----------------------------------------
612
613BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {}
614
615BrowserInit::LaunchWithProfile::Tab::~Tab() {}
616
617
618// BrowserInit::LaunchWithProfile ---------------------------------------------
619
620BrowserInit::LaunchWithProfile::LaunchWithProfile(
621    const FilePath& cur_dir,
622    const CommandLine& command_line)
623        : cur_dir_(cur_dir),
624          command_line_(command_line),
625          profile_(NULL),
626          browser_init_(NULL) {
627}
628
629BrowserInit::LaunchWithProfile::LaunchWithProfile(
630    const FilePath& cur_dir,
631    const CommandLine& command_line,
632    BrowserInit* browser_init)
633        : cur_dir_(cur_dir),
634          command_line_(command_line),
635          profile_(NULL),
636          browser_init_(browser_init) {
637}
638
639BrowserInit::LaunchWithProfile::~LaunchWithProfile() {
640}
641
642bool BrowserInit::LaunchWithProfile::Launch(
643    Profile* profile,
644    const std::vector<GURL>& urls_to_open,
645    bool process_startup) {
646  DCHECK(profile);
647  profile_ = profile;
648
649  if (command_line_.HasSwitch(switches::kDnsLogDetails))
650    chrome_browser_net::EnablePredictorDetailedLog(true);
651  if (command_line_.HasSwitch(switches::kDnsPrefetchDisable))
652    chrome_browser_net::EnablePredictor(false);
653
654  if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit))
655    base::StatisticsRecorder::set_dump_on_exit(true);
656
657  if (command_line_.HasSwitch(switches::kRemoteShellPort)) {
658    std::string port_str =
659        command_line_.GetSwitchValueASCII(switches::kRemoteShellPort);
660    int64 port;
661    if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) {
662      g_browser_process->InitDevToolsLegacyProtocolHandler(
663          static_cast<int>(port));
664    } else {
665      DLOG(WARNING) << "Invalid remote shell port number " << port;
666    }
667  } else if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) {
668    std::string port_str =
669        command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort);
670    int64 port;
671    if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) {
672      g_browser_process->InitDevToolsHttpProtocolHandler(
673          "127.0.0.1",
674          static_cast<int>(port),
675          "");
676    } else {
677      DLOG(WARNING) << "Invalid http debugger port number " << port;
678    }
679  }
680
681  if (command_line_.HasSwitch(switches::kUserAgent)) {
682    webkit_glue::SetUserAgent(command_line_.GetSwitchValueASCII(
683        switches::kUserAgent));
684  }
685
686  // Open the required browser windows and tabs. First, see if
687  // we're being run as an application window. If so, the user
688  // opened an app shortcut.  Don't restore tabs or open initial
689  // URLs in that case. The user should see the window as an app,
690  // not as chrome.
691  if (OpenApplicationWindow(profile)) {
692    RecordLaunchModeHistogram(LM_AS_WEBAPP);
693  } else {
694    RecordLaunchModeHistogram(urls_to_open.empty()?
695                              LM_TO_BE_DECIDED : LM_WITH_URLS);
696    ProcessLaunchURLs(process_startup, urls_to_open);
697
698    // If this is an app launch, but we didn't open an app window, it may
699    // be an app tab.
700    OpenApplicationTab(profile);
701
702    if (process_startup) {
703      if (browser_defaults::kOSSupportsOtherBrowsers &&
704          !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) {
705        // Check whether we are the default browser.
706        CheckDefaultBrowser(profile);
707      }
708#if defined(OS_MACOSX)
709      // Check whether the auto-update system needs to be promoted from user
710      // to system.
711      KeystoneInfoBar::PromotionInfoBar(profile);
712#endif
713    }
714  }
715
716#if defined(OS_WIN)
717  // Print the selected page if the command line switch exists. Note that the
718  // current selected tab would be the page which will be printed.
719  if (command_line_.HasSwitch(switches::kPrint)) {
720    Browser* browser = BrowserList::GetLastActive();
721    browser->Print();
722  }
723#endif
724
725  // If we're recording or playing back, startup the EventRecorder now
726  // unless otherwise specified.
727  if (!command_line_.HasSwitch(switches::kNoEvents)) {
728    FilePath script_path;
729    PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path);
730
731    bool record_mode = command_line_.HasSwitch(switches::kRecordMode);
732    bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode);
733
734    if (record_mode && chrome::kRecordModeEnabled)
735      base::EventRecorder::current()->StartRecording(script_path);
736    if (playback_mode)
737      base::EventRecorder::current()->StartPlayback(script_path);
738  }
739
740#if defined(OS_WIN)
741  if (process_startup)
742    ShellIntegration::MigrateChromiumShortcuts();
743#endif  // defined(OS_WIN)
744
745  return true;
746}
747
748bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url,
749                                                 std::string* app_id) {
750  if (command_line_.HasSwitch(switches::kApp)) {
751    if (app_url)
752      *app_url = command_line_.GetSwitchValueASCII(switches::kApp);
753    return true;
754  }
755  if (command_line_.HasSwitch(switches::kAppId)) {
756    if (app_id)
757      *app_id = command_line_.GetSwitchValueASCII(switches::kAppId);
758    return true;
759  }
760  return false;
761}
762
763bool BrowserInit::LaunchWithProfile::OpenApplicationTab(Profile* profile) {
764  std::string app_id;
765  // App shortcuts to URLs always open in an app window.  Because this
766  // function will open an app that should be in a tab, there is no need
767  // to look at the app URL.  OpenApplicationWindow() will open app url
768  // shortcuts.
769  if (!IsAppLaunch(NULL, &app_id) || app_id.empty())
770    return false;
771
772  extension_misc::LaunchContainer launch_container;
773  const Extension* extension;
774  if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
775    return false;
776
777  // If the user doesn't want to open a tab, fail.
778  if (launch_container != extension_misc::LAUNCH_TAB)
779    return false;
780
781  RecordCmdLineAppHistogram();
782
783  TabContents* app_tab = Browser::OpenApplicationTab(profile, extension, NULL);
784  return (app_tab != NULL);
785}
786
787bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) {
788  std::string url_string, app_id;
789  if (!IsAppLaunch(&url_string, &app_id))
790    return false;
791
792  // This can fail if the app_id is invalid.  It can also fail if the
793  // extension is external, and has not yet been installed.
794  // TODO(skerner): Do something reasonable here. Pop up a warning panel?
795  // Open an URL to the gallery page of the extension id?
796  if (!app_id.empty()) {
797    extension_misc::LaunchContainer launch_container;
798    const Extension* extension;
799    if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container))
800      return false;
801
802    // TODO(skerner): Could pass in |extension| and |launch_container|,
803    // and avoid calling GetAppLaunchContainer() both here and in
804    // OpenApplicationTab().
805
806    if (launch_container == extension_misc::LAUNCH_TAB)
807      return false;
808
809    RecordCmdLineAppHistogram();
810    TabContents* tab_in_app_window = Browser::OpenApplication(
811        profile, extension, launch_container, NULL);
812    return (tab_in_app_window != NULL);
813  }
814
815  if (url_string.empty())
816    return false;
817
818#if defined(OS_WIN)  // Fix up Windows shortcuts.
819  ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%");
820#endif
821  GURL url(url_string);
822
823  // Restrict allowed URLs for --app switch.
824  if (!url.is_empty() && url.is_valid()) {
825    ChildProcessSecurityPolicy *policy =
826        ChildProcessSecurityPolicy::GetInstance();
827    if (policy->IsWebSafeScheme(url.scheme()) ||
828        url.SchemeIs(chrome::kFileScheme)) {
829
830      if (profile->GetExtensionService()->IsInstalledApp(url)) {
831        RecordCmdLineAppHistogram();
832      } else {
833        UMA_HISTOGRAM_ENUMERATION(
834            extension_misc::kAppLaunchHistogram,
835            extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY,
836            extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
837      }
838      TabContents* app_tab = Browser::OpenAppShortcutWindow(
839          profile,
840          url,
841          true);  // Update app info.
842      return (app_tab != NULL);
843    }
844  }
845  return false;
846}
847
848void BrowserInit::LaunchWithProfile::ProcessLaunchURLs(
849    bool process_startup,
850    const std::vector<GURL>& urls_to_open) {
851  // If we're starting up in "background mode" (no open browser window) then
852  // don't open any browser windows.
853  if (process_startup && command_line_.HasSwitch(switches::kNoStartupWindow))
854    return;
855
856  if (process_startup && ProcessStartupURLs(urls_to_open)) {
857    // ProcessStartupURLs processed the urls, nothing else to do.
858    return;
859  }
860
861  if (!process_startup &&
862      (profile_->GetSessionService() &&
863       profile_->GetSessionService()->RestoreIfNecessary(urls_to_open))) {
864    // We're already running and session restore wanted to run. This can happen
865    // at various points, such as if there is only an app window running and the
866    // user double clicked the chrome icon. Return so we don't open the urls.
867    return;
868  }
869
870  // Session restore didn't occur, open the urls.
871
872  Browser* browser = NULL;
873  std::vector<GURL> adjust_urls = urls_to_open;
874  if (adjust_urls.empty())
875    AddStartupURLs(&adjust_urls);
876  else if (!command_line_.HasSwitch(switches::kOpenInNewWindow))
877    browser = BrowserList::GetLastActiveWithProfile(profile_);
878
879  browser = OpenURLsInBrowser(browser, process_startup, adjust_urls);
880  if (process_startup)
881    AddInfoBarsIfNecessary(browser);
882}
883
884bool BrowserInit::LaunchWithProfile::ProcessStartupURLs(
885    const std::vector<GURL>& urls_to_open) {
886  SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_);
887  if (command_line_.HasSwitch(switches::kTestingChannelID) &&
888      !command_line_.HasSwitch(switches::kRestoreLastSession) &&
889      browser_defaults::kDefaultSessionStartupType !=
890      SessionStartupPref::DEFAULT) {
891    // When we have non DEFAULT session start type, then we won't open up a
892    // fresh session. But none of the tests are written with this in mind, so
893    // we explicitly ignore it during testing.
894    return false;
895  }
896
897  if (pref.type == SessionStartupPref::LAST) {
898    if (!profile_->DidLastSessionExitCleanly() &&
899        !command_line_.HasSwitch(switches::kRestoreLastSession)) {
900      // The last session crashed. It's possible automatically loading the
901      // page will trigger another crash, locking the user out of chrome.
902      // To avoid this, don't restore on startup but instead show the crashed
903      // infobar.
904      return false;
905    }
906    Browser* browser =
907        SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open);
908    AddInfoBarsIfNecessary(browser);
909    return true;
910  }
911
912  std::vector<Tab> tabs = PinnedTabCodec::ReadPinnedTabs(profile_);
913
914  RecordAppLaunches(profile_, urls_to_open, tabs);
915
916  if (!urls_to_open.empty()) {
917    // If urls were specified on the command line, use them.
918    UrlsToTabs(urls_to_open, &tabs);
919  } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty()) {
920    // Only use the set of urls specified in preferences if nothing was
921    // specified on the command line. Filter out any urls that are to be
922    // restored by virtue of having been previously pinned.
923    AddUniqueURLs(pref.urls, &tabs);
924  } else if (pref.type == SessionStartupPref::DEFAULT && !tabs.empty()) {
925    // Make sure the home page is opened even if there are pinned tabs.
926    std::vector<GURL> urls;
927    AddStartupURLs(&urls);
928    UrlsToTabs(urls, &tabs);
929  }
930
931  if (tabs.empty())
932    return false;
933
934  Browser* browser = OpenTabsInBrowser(NULL, true, tabs);
935  AddInfoBarsIfNecessary(browser);
936  return true;
937}
938
939void BrowserInit::LaunchWithProfile::AddUniqueURLs(
940    const std::vector<GURL>& urls,
941    std::vector<Tab>* tabs) {
942  size_t num_existing_tabs = tabs->size();
943  for (size_t i = 0; i < urls.size(); ++i) {
944    bool in_tabs = false;
945    for (size_t j = 0; j < num_existing_tabs; ++j) {
946      if (urls[i] == (*tabs)[j].url) {
947        in_tabs = true;
948        break;
949      }
950    }
951    if (!in_tabs) {
952      BrowserInit::LaunchWithProfile::Tab tab;
953      tab.is_pinned = false;
954      tab.url = urls[i];
955      tabs->push_back(tab);
956    }
957  }
958}
959
960Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser(
961    Browser* browser,
962    bool process_startup,
963    const std::vector<GURL>& urls) {
964  std::vector<Tab> tabs;
965  UrlsToTabs(urls, &tabs);
966  return OpenTabsInBrowser(browser, process_startup, tabs);
967}
968
969Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser(
970        Browser* browser,
971        bool process_startup,
972        const std::vector<Tab>& tabs) {
973  DCHECK(!tabs.empty());
974  // If we don't yet have a profile, try to use the one we're given from
975  // |browser|. While we may not end up actually using |browser| (since it
976  // could be a popup window), we can at least use the profile.
977  if (!profile_ && browser)
978    profile_ = browser->profile();
979
980  if (!browser || browser->type() != Browser::TYPE_NORMAL) {
981    browser = Browser::Create(profile_);
982  } else {
983#if defined(TOOLKIT_GTK)
984    // Setting the time of the last action on the window here allows us to steal
985    // focus, which is what the user wants when opening a new tab in an existing
986    // browser window.
987    gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle());
988#endif
989  }
990
991#if !defined(OS_MACOSX)
992  // In kiosk mode, we want to always be fullscreen, so switch to that now.
993  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
994    browser->ToggleFullscreenMode();
995#endif
996
997  bool first_tab = true;
998  for (size_t i = 0; i < tabs.size(); ++i) {
999    // We skip URLs that we'd have to launch an external protocol handler for.
1000    // This avoids us getting into an infinite loop asking ourselves to open
1001    // a URL, should the handler be (incorrectly) configured to be us. Anyone
1002    // asking us to open such a URL should really ask the handler directly.
1003    if (!process_startup && !net::URLRequest::IsHandledURL(tabs[i].url))
1004      continue;
1005
1006    int add_types = first_tab ? TabStripModel::ADD_ACTIVE :
1007                                TabStripModel::ADD_NONE;
1008    add_types |= TabStripModel::ADD_FORCE_INDEX;
1009    if (tabs[i].is_pinned)
1010      add_types |= TabStripModel::ADD_PINNED;
1011    int index = browser->GetIndexForInsertionDuringRestore(i);
1012
1013    browser::NavigateParams params(browser, tabs[i].url,
1014                                   PageTransition::START_PAGE);
1015    params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
1016    params.tabstrip_index = index;
1017    params.tabstrip_add_types = add_types;
1018    params.extension_app_id = tabs[i].app_id;
1019    browser::Navigate(&params);
1020
1021    first_tab = false;
1022  }
1023  browser->window()->Show();
1024  // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial
1025  //                focus explicitly.
1026  browser->GetSelectedTabContents()->view()->SetInitialFocus();
1027
1028  return browser;
1029}
1030
1031void BrowserInit::LaunchWithProfile::AddInfoBarsIfNecessary(Browser* browser) {
1032  if (!browser || !profile_ || browser->tab_count() == 0)
1033    return;
1034
1035  TabContents* tab_contents = browser->GetSelectedTabContents();
1036  AddCrashedInfoBarIfNecessary(tab_contents);
1037  AddBadFlagsInfoBarIfNecessary(tab_contents);
1038  AddDNSCertProvenanceCheckingWarningInfoBarIfNecessary(tab_contents);
1039  AddObsoleteSystemInfoBarIfNecessary(tab_contents);
1040}
1041
1042void BrowserInit::LaunchWithProfile::AddCrashedInfoBarIfNecessary(
1043    TabContents* tab) {
1044  // Assume that if the user is launching incognito they were previously
1045  // running incognito so that we have nothing to restore from.
1046  if (!profile_->DidLastSessionExitCleanly() &&
1047      !profile_->IsOffTheRecord()) {
1048    // The last session didn't exit cleanly. Show an infobar to the user
1049    // so that they can restore if they want. The delegate deletes itself when
1050    // it is closed.
1051    tab->AddInfoBar(new SessionCrashedInfoBarDelegate(tab));
1052  }
1053}
1054
1055void BrowserInit::LaunchWithProfile::AddBadFlagsInfoBarIfNecessary(
1056    TabContents* tab) {
1057  // Unsupported flags for which to display a warning that "stability and
1058  // security will suffer".
1059  static const char* kBadFlags[] = {
1060    // These imply disabling the sandbox.
1061    switches::kSingleProcess,
1062    switches::kNoSandbox,
1063    switches::kInProcessWebGL,
1064    // These are scary features for developers that shouldn't be turned on
1065    // persistently.
1066    switches::kEnableNaCl,
1067    NULL
1068  };
1069
1070  const char* bad_flag = NULL;
1071  for (const char** flag = kBadFlags; *flag; ++flag) {
1072    if (command_line_.HasSwitch(*flag)) {
1073      bad_flag = *flag;
1074      break;
1075    }
1076  }
1077
1078  if (bad_flag) {
1079    tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab, NULL,
1080        l10n_util::GetStringFUTF16(IDS_BAD_FLAGS_WARNING_MESSAGE,
1081                                   UTF8ToUTF16(std::string("--") + bad_flag)),
1082        false));
1083  }
1084}
1085
1086class LearnMoreInfoBar : public LinkInfoBarDelegate {
1087 public:
1088  explicit LearnMoreInfoBar(TabContents* tab_contents,
1089                            const string16& message,
1090                            const GURL& url);
1091  virtual ~LearnMoreInfoBar();
1092
1093  virtual string16 GetMessageTextWithOffset(size_t* link_offset) const OVERRIDE;
1094  virtual string16 GetLinkText() const OVERRIDE;
1095  virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE;
1096
1097 private:
1098  TabContents* const tab_contents_;
1099  string16 message_;
1100  GURL learn_more_url_;
1101
1102  DISALLOW_COPY_AND_ASSIGN(LearnMoreInfoBar);
1103};
1104
1105LearnMoreInfoBar::LearnMoreInfoBar(TabContents* tab_contents,
1106                                   const string16& message,
1107                                   const GURL& url)
1108    : LinkInfoBarDelegate(tab_contents),
1109      tab_contents_(tab_contents),
1110      message_(message),
1111      learn_more_url_(url) {
1112}
1113
1114LearnMoreInfoBar::~LearnMoreInfoBar() {
1115}
1116
1117string16 LearnMoreInfoBar::GetMessageTextWithOffset(size_t* link_offset) const {
1118  string16 text = message_;
1119  text.push_back(' ');  // Add a space before the following link.
1120  *link_offset = text.size();
1121  return text;
1122}
1123
1124string16 LearnMoreInfoBar::GetLinkText() const {
1125  return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
1126}
1127
1128bool LearnMoreInfoBar::LinkClicked(WindowOpenDisposition disposition) {
1129  tab_contents_->OpenURL(learn_more_url_, GURL(), disposition,
1130                         PageTransition::LINK);
1131  return false;
1132}
1133
1134// This is the page which provides information on DNS certificate provenance
1135// checking.
1136void BrowserInit::LaunchWithProfile::
1137    AddDNSCertProvenanceCheckingWarningInfoBarIfNecessary(TabContents* tab) {
1138  if (!command_line_.HasSwitch(switches::kEnableDNSCertProvenanceChecking))
1139    return;
1140
1141  const char* kLearnMoreURL =
1142      "http://dev.chromium.org/dnscertprovenancechecking";
1143  string16 message = l10n_util::GetStringUTF16(
1144      IDS_DNS_CERT_PROVENANCE_CHECKING_WARNING_MESSAGE);
1145  tab->AddInfoBar(new LearnMoreInfoBar(tab,
1146                                       message,
1147                                       GURL(kLearnMoreURL)));
1148}
1149
1150void BrowserInit::LaunchWithProfile::AddObsoleteSystemInfoBarIfNecessary(
1151    TabContents* tab) {
1152#if defined(TOOLKIT_USES_GTK)
1153  // We've deprecated support for Ubuntu Hardy.  Rather than attempting to
1154  // determine whether you're using that, we instead key off the GTK version;
1155  // this will also deprecate other distributions (including variants of Ubuntu)
1156  // that are of a similar age.
1157  // Version key:
1158  //   Ubuntu Hardy: GTK 2.12
1159  //   RHEL 6:       GTK 2.18
1160  //   Ubuntu Lucid: GTK 2.20
1161  if (gtk_check_version(2, 18, 0)) {
1162    string16 message =
1163        l10n_util::GetStringFUTF16(IDS_SYSTEM_OBSOLETE_MESSAGE,
1164                                   l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
1165    // Link to an article in the help center on minimum system requirements.
1166    const char* kLearnMoreURL =
1167        "http://www.google.com/support/chrome/bin/answer.py?answer=95411";
1168    tab->AddInfoBar(new LearnMoreInfoBar(tab,
1169                                         message,
1170                                         GURL(kLearnMoreURL)));
1171  }
1172#endif
1173}
1174
1175void BrowserInit::LaunchWithProfile::AddStartupURLs(
1176    std::vector<GURL>* startup_urls) const {
1177  // If we have urls specified beforehand (i.e. from command line) use them
1178  // and nothing else.
1179  if (!startup_urls->empty())
1180    return;
1181  // If we have urls specified by the first run master preferences use them
1182  // and nothing else.
1183  if (browser_init_) {
1184    if (!browser_init_->first_run_tabs_.empty()) {
1185      std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin();
1186      while (it != browser_init_->first_run_tabs_.end()) {
1187        // Replace magic names for the actual urls.
1188        if (it->host() == "new_tab_page") {
1189          startup_urls->push_back(GURL(chrome::kChromeUINewTabURL));
1190        } else if (it->host() == "welcome_page") {
1191          startup_urls->push_back(GetWelcomePageURL());
1192        } else {
1193          startup_urls->push_back(*it);
1194        }
1195        ++it;
1196      }
1197      browser_init_->first_run_tabs_.clear();
1198      return;
1199    }
1200  }
1201
1202  // Otherwise open at least the new tab page (and the welcome page, if this
1203  // is the first time the browser is being started), or the set of URLs
1204  // specified on the command line.
1205  startup_urls->push_back(GURL());  // New tab page.
1206  PrefService* prefs = g_browser_process->local_state();
1207  if (prefs->FindPreference(prefs::kShouldShowWelcomePage) &&
1208      prefs->GetBoolean(prefs::kShouldShowWelcomePage)) {
1209    // Reset the preference so we don't show the welcome page next time.
1210    prefs->ClearPref(prefs::kShouldShowWelcomePage);
1211    startup_urls->push_back(GetWelcomePageURL());
1212  }
1213}
1214
1215void BrowserInit::LaunchWithProfile::CheckDefaultBrowser(Profile* profile) {
1216  // We do not check if we are the default browser if:
1217  // - the user said "don't ask me again" on the infobar earlier.
1218  // - this is the first launch after the first run flow.
1219  // - There is a policy in control of this setting.
1220  if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser) ||
1221      FirstRun::IsChromeFirstRun()) {
1222    return;
1223  }
1224  if (g_browser_process->local_state()->IsManagedPreference(
1225      prefs::kDefaultBrowserSettingEnabled)) {
1226    if (g_browser_process->local_state()->GetBoolean(
1227        prefs::kDefaultBrowserSettingEnabled)) {
1228      BrowserThread::PostTask(
1229          BrowserThread::FILE, FROM_HERE, NewRunnableFunction(
1230              &ShellIntegration::SetAsDefaultBrowser));
1231    } else {
1232      // TODO(pastarmovj): We can't really do anything meaningful here yet but
1233      // just prevent showing the infobar.
1234    }
1235    return;
1236  }
1237  BrowserThread::PostTask(
1238      BrowserThread::FILE, FROM_HERE, new CheckDefaultBrowserTask());
1239}
1240
1241std::vector<GURL> BrowserInit::GetURLsFromCommandLine(
1242    const CommandLine& command_line,
1243    const FilePath& cur_dir,
1244    Profile* profile) {
1245  std::vector<GURL> urls;
1246  const std::vector<CommandLine::StringType>& params = command_line.args();
1247
1248  for (size_t i = 0; i < params.size(); ++i) {
1249    FilePath param = FilePath(params[i]);
1250    // Handle Vista way of searching - "? <search-term>"
1251    if (param.value().size() > 2 &&
1252        param.value()[0] == '?' && param.value()[1] == ' ') {
1253      const TemplateURL* default_provider =
1254          profile->GetTemplateURLModel()->GetDefaultSearchProvider();
1255      if (default_provider && default_provider->url()) {
1256        const TemplateURLRef* search_url = default_provider->url();
1257        DCHECK(search_url->SupportsReplacement());
1258        string16 search_term = param.LossyDisplayName().substr(2);
1259        urls.push_back(GURL(search_url->ReplaceSearchTerms(
1260                                *default_provider, search_term,
1261                                TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
1262                                string16())));
1263        continue;
1264      }
1265    }
1266
1267    // Otherwise, fall through to treating it as a URL.
1268
1269    // This will create a file URL or a regular URL.
1270    // This call can (in rare circumstances) block the UI thread.
1271    // Allow it until this bug is fixed.
1272    //  http://code.google.com/p/chromium/issues/detail?id=60641
1273    GURL url;
1274    {
1275      base::ThreadRestrictions::ScopedAllowIO allow_io;
1276      url = URLFixerUpper::FixupRelativeFile(cur_dir, param);
1277    }
1278    // Exclude dangerous schemes.
1279    if (url.is_valid()) {
1280      ChildProcessSecurityPolicy *policy =
1281          ChildProcessSecurityPolicy::GetInstance();
1282      if (policy->IsWebSafeScheme(url.scheme()) ||
1283          url.SchemeIs(chrome::kFileScheme) ||
1284#if defined(OS_CHROMEOS)
1285          // In ChromeOS, allow a settings page to be specified on the
1286          // command line. See ExistingUserController::OnLoginSuccess.
1287          (url.spec().find(chrome::kChromeUISettingsURL) == 0) ||
1288#endif
1289          (url.spec().compare(chrome::kAboutBlankURL) == 0)) {
1290        urls.push_back(url);
1291      }
1292    }
1293  }
1294  return urls;
1295}
1296
1297bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
1298                                     const FilePath& cur_dir,
1299                                     bool process_startup,
1300                                     Profile* profile,
1301                                     int* return_code,
1302                                     BrowserInit* browser_init) {
1303  DCHECK(profile);
1304  if (process_startup) {
1305    if (command_line.HasSwitch(switches::kDisablePromptOnRepost))
1306      NavigationController::DisablePromptOnRepost();
1307
1308    // Look for the testing channel ID ONLY during process startup
1309    if (command_line.HasSwitch(switches::kTestingChannelID)) {
1310      std::string testing_channel_id = command_line.GetSwitchValueASCII(
1311          switches::kTestingChannelID);
1312      // TODO(sanjeevr) Check if we need to make this a singleton for
1313      // compatibility with the old testing code
1314      // If there are any extra parameters, we expect each one to generate a
1315      // new tab; if there are none then we get one homepage tab.
1316      int expected_tab_count = 1;
1317      if (command_line.HasSwitch(switches::kNoStartupWindow)) {
1318        expected_tab_count = 0;
1319#if defined(OS_CHROMEOS)
1320      // kLoginManager will cause Chrome to start up with the ChromeOS login
1321      // screen instead of a browser window, so it won't load any tabs.
1322      } else if (command_line.HasSwitch(switches::kLoginManager)) {
1323        expected_tab_count = 0;
1324#endif
1325      } else if (command_line.HasSwitch(switches::kRestoreLastSession)) {
1326        std::string restore_session_value(
1327            command_line.GetSwitchValueASCII(switches::kRestoreLastSession));
1328        base::StringToInt(restore_session_value, &expected_tab_count);
1329      } else {
1330        std::vector<GURL> urls_to_open = GetURLsFromCommandLine(
1331            command_line, cur_dir, profile);
1332        expected_tab_count =
1333            std::max(1, static_cast<int>(urls_to_open.size()));
1334      }
1335      if (!CreateAutomationProvider<TestingAutomationProvider>(
1336          testing_channel_id,
1337          profile,
1338          static_cast<size_t>(expected_tab_count)))
1339        return false;
1340    }
1341  }
1342
1343  bool silent_launch = false;
1344
1345  if (command_line.HasSwitch(switches::kAutomationClientChannelID)) {
1346    std::string automation_channel_id = command_line.GetSwitchValueASCII(
1347        switches::kAutomationClientChannelID);
1348    // If there are any extra parameters, we expect each one to generate a
1349    // new tab; if there are none then we have no tabs
1350    std::vector<GURL> urls_to_open = GetURLsFromCommandLine(
1351        command_line, cur_dir, profile);
1352    size_t expected_tabs =
1353        std::max(static_cast<int>(urls_to_open.size()), 0);
1354    if (expected_tabs == 0)
1355      silent_launch = true;
1356
1357    if (command_line.HasSwitch(switches::kChromeFrame)) {
1358      if (!CreateAutomationProvider<ChromeFrameAutomationProvider>(
1359          automation_channel_id, profile, expected_tabs))
1360        return false;
1361    } else {
1362      if (!CreateAutomationProvider<AutomationProvider>(
1363          automation_channel_id, profile, expected_tabs))
1364        return false;
1365    }
1366  }
1367
1368  // If we have been invoked to display a desktop notification on behalf of
1369  // the service process, we do not want to open any browser windows.
1370  if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) {
1371    silent_launch = true;
1372    profile->GetCloudPrintProxyService()->ShowTokenExpiredNotification();
1373  }
1374
1375  // If we are just displaying a print dialog we shouldn't open browser
1376  // windows.
1377  if (print_dialog_cloud::CreatePrintDialogFromCommandLine(command_line)) {
1378    silent_launch = true;
1379  }
1380
1381  if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) {
1382    std::string allowed_ports =
1383        command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts);
1384    net::SetExplicitlyAllowedPorts(allowed_ports);
1385  }
1386
1387#if defined(OS_CHROMEOS)
1388  // The browser will be launched after the user logs in.
1389  if (command_line.HasSwitch(switches::kLoginManager) ||
1390      command_line.HasSwitch(switches::kLoginPassword)) {
1391    silent_launch = true;
1392  }
1393#endif
1394
1395#if defined(HAVE_XINPUT2) && defined(TOUCH_UI)
1396  // Get a list of pointer-devices that should be treated as touch-devices.
1397  // TODO(sad): Instead of/in addition to getting the list from the
1398  // command-line, query X for a list of touch devices.
1399  std::string touch_devices =
1400    command_line.GetSwitchValueASCII(switches::kTouchDevices);
1401
1402  if (!touch_devices.empty()) {
1403    std::vector<std::string> devs;
1404    std::vector<unsigned int> device_ids;
1405    unsigned int devid;
1406    base::SplitString(touch_devices, ',', &devs);
1407    for (std::vector<std::string>::iterator iter = devs.begin();
1408        iter != devs.end(); ++iter) {
1409      if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) {
1410        device_ids.push_back(devid);
1411      } else {
1412        DLOG(WARNING) << "Invalid touch-device id: " << *iter;
1413      }
1414    }
1415    views::SetTouchDeviceList(device_ids);
1416  }
1417#endif
1418
1419  // If we don't want to launch a new browser window or tab (in the case
1420  // of an automation request), we are done here.
1421  if (!silent_launch) {
1422    return browser_init->LaunchBrowser(
1423        command_line, profile, cur_dir, process_startup, return_code);
1424  }
1425  return true;
1426}
1427
1428template <class AutomationProviderClass>
1429bool BrowserInit::CreateAutomationProvider(const std::string& channel_id,
1430                                           Profile* profile,
1431                                           size_t expected_tabs) {
1432  scoped_refptr<AutomationProviderClass> automation =
1433      new AutomationProviderClass(profile);
1434
1435  if (!automation->InitializeChannel(channel_id))
1436    return false;
1437  automation->SetExpectedTabCount(expected_tabs);
1438
1439  AutomationProviderList* list =
1440      g_browser_process->InitAutomationProviderList();
1441  DCHECK(list);
1442  list->AddProvider(automation);
1443
1444  return true;
1445}
1446