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