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