session_restore.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sessions/session_restore.h"
6
7#include <algorithm>
8#include <list>
9#include <set>
10#include <vector>
11
12#include "base/callback.h"
13#include "base/command_line.h"
14#include "base/scoped_ptr.h"
15#include "base/stl_util-inl.h"
16#include "base/string_util.h"
17#include "chrome/browser/extensions/extension_service.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/sessions/session_service.h"
20#include "chrome/browser/sessions/session_types.h"
21#include "chrome/browser/tab_contents/navigation_controller.h"
22#include "chrome/browser/tab_contents/tab_contents.h"
23#include "chrome/browser/tab_contents/tab_contents_view.h"
24#include "chrome/browser/tabs/tab_strip_model.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_list.h"
27#include "chrome/browser/ui/browser_navigator.h"
28#include "chrome/browser/ui/browser_window.h"
29#include "chrome/common/chrome_switches.h"
30#include "chrome/common/notification_registrar.h"
31#include "chrome/common/notification_service.h"
32
33#if defined(OS_CHROMEOS)
34#include "chrome/browser/chromeos/boot_times_loader.h"
35#include "chrome/browser/chromeos/network_state_notifier.h"
36#endif
37
38// Are we in the process of restoring?
39static bool restoring = false;
40
41namespace {
42
43// TabLoader ------------------------------------------------------------------
44
45// Initial delay (see class decription for details).
46static const int kInitialDelayTimerMS = 100;
47
48// TabLoader is responsible for loading tabs after session restore creates
49// tabs. New tabs are loaded after the current tab finishes loading, or a delay
50// is reached (initially kInitialDelayTimerMS). If the delay is reached before
51// a tab finishes loading a new tab is loaded and the time of the delay
52// doubled. When all tabs are loading TabLoader deletes itself.
53//
54// This is not part of SessionRestoreImpl so that synchronous destruction
55// of SessionRestoreImpl doesn't have timing problems.
56class TabLoader : public NotificationObserver {
57 public:
58  typedef std::list<NavigationController*> TabsToLoad;
59
60  TabLoader();
61  ~TabLoader();
62
63  // Schedules a tab for loading.
64  void ScheduleLoad(NavigationController* controller);
65
66  // Invokes |LoadNextTab| to load a tab.
67  //
68  // This must be invoked once to start loading.
69  void StartLoading();
70
71 private:
72  typedef std::set<NavigationController*> TabsLoading;
73
74  // Loads the next tab. If there are no more tabs to load this deletes itself,
75  // otherwise |force_load_timer_| is restarted.
76  void LoadNextTab();
77
78  // NotificationObserver method. Removes the specified tab and loads the next
79  // tab.
80  virtual void Observe(NotificationType type,
81                       const NotificationSource& source,
82                       const NotificationDetails& details);
83
84  // Removes the listeners from the specified tab and removes the tab from
85  // the set of tabs to load and list of tabs we're waiting to get a load
86  // from.
87  void RemoveTab(NavigationController* tab);
88
89  // Invoked from |force_load_timer_|. Doubles |force_load_delay_| and invokes
90  // |LoadNextTab| to load the next tab
91  void ForceLoadTimerFired();
92
93  NotificationRegistrar registrar_;
94
95  // Current delay before a new tab is loaded. See class description for
96  // details.
97  int64 force_load_delay_;
98
99  // Has Load been invoked?
100  bool loading_;
101
102  // The set of tabs we've initiated loading on. This does NOT include the
103  // selected tabs.
104  TabsLoading tabs_loading_;
105
106  // The tabs we need to load.
107  TabsToLoad tabs_to_load_;
108
109  base::OneShotTimer<TabLoader> force_load_timer_;
110
111  DISALLOW_COPY_AND_ASSIGN(TabLoader);
112};
113
114TabLoader::TabLoader()
115    : force_load_delay_(kInitialDelayTimerMS),
116      loading_(false) {
117}
118
119TabLoader::~TabLoader() {
120  DCHECK(tabs_to_load_.empty() && tabs_loading_.empty());
121}
122
123void TabLoader::ScheduleLoad(NavigationController* controller) {
124  if (controller) {
125    DCHECK(find(tabs_to_load_.begin(), tabs_to_load_.end(), controller) ==
126           tabs_to_load_.end());
127    tabs_to_load_.push_back(controller);
128    registrar_.Add(this, NotificationType::TAB_CLOSED,
129                   Source<NavigationController>(controller));
130    registrar_.Add(this, NotificationType::LOAD_STOP,
131                   Source<NavigationController>(controller));
132  } else {
133    // Should never get a NULL tab.
134    NOTREACHED();
135  }
136}
137
138void TabLoader::StartLoading() {
139#if defined(OS_CHROMEOS)
140  if (chromeos::NetworkStateNotifier::is_connected()) {
141    loading_ = true;
142    LoadNextTab();
143  } else {
144    // Start listening to network state notification now.
145    registrar_.Add(this, NotificationType::NETWORK_STATE_CHANGED,
146                   NotificationService::AllSources());
147  }
148#else
149  loading_ = true;
150  LoadNextTab();
151#endif
152}
153
154void TabLoader::LoadNextTab() {
155  if (!tabs_to_load_.empty()) {
156    NavigationController* tab = tabs_to_load_.front();
157    DCHECK(tab);
158    tabs_loading_.insert(tab);
159    tabs_to_load_.pop_front();
160    tab->LoadIfNecessary();
161    if (tab->tab_contents()) {
162      int tab_index;
163      Browser* browser = Browser::GetBrowserForController(tab, &tab_index);
164      if (browser && browser->selected_index() != tab_index) {
165        // By default tabs are marked as visible. As only the selected tab is
166        // visible we need to explicitly tell non-selected tabs they are hidden.
167        // Without this call non-selected tabs are not marked as backgrounded.
168        //
169        // NOTE: We need to do this here rather than when the tab is added to
170        // the Browser as at that time not everything has been created, so that
171        // the call would do nothing.
172        tab->tab_contents()->WasHidden();
173      }
174    }
175  }
176
177  if (tabs_to_load_.empty()) {
178    tabs_loading_.clear();
179    delete this;
180    return;
181  }
182
183  if (force_load_timer_.IsRunning())
184    force_load_timer_.Stop();
185  force_load_timer_.Start(
186      base::TimeDelta::FromMilliseconds(force_load_delay_),
187      this, &TabLoader::ForceLoadTimerFired);
188}
189
190void TabLoader::Observe(NotificationType type,
191                        const NotificationSource& source,
192                        const NotificationDetails& details) {
193  switch (type.value) {
194#if defined(OS_CHROMEOS)
195    case NotificationType::NETWORK_STATE_CHANGED: {
196      chromeos::NetworkStateDetails* state_details =
197          Details<chromeos::NetworkStateDetails>(details).ptr();
198      switch (state_details->state()) {
199        case chromeos::NetworkStateDetails::CONNECTED:
200          if (!loading_) {
201            loading_ = true;
202            LoadNextTab();
203          }
204          // Start loading
205          break;
206        case chromeos::NetworkStateDetails::CONNECTING:
207        case chromeos::NetworkStateDetails::DISCONNECTED:
208          // Disconnected while loading. Set loading_ false so
209          // that it stops trying to load next tab.
210          loading_ = false;
211          break;
212        default:
213          NOTREACHED() << "Unknown nework state notification:"
214                       << state_details->state();
215      }
216      break;
217    }
218#endif
219    case NotificationType::TAB_CLOSED:
220    case NotificationType::LOAD_STOP: {
221      NavigationController* tab = Source<NavigationController>(source).ptr();
222      RemoveTab(tab);
223      if (loading_) {
224        LoadNextTab();
225        // WARNING: if there are no more tabs to load, we have been deleted.
226      } else if (tabs_to_load_.empty()) {
227        tabs_loading_.clear();
228        delete this;
229        return;
230      }
231      break;
232    }
233    default:
234      NOTREACHED() << "Unknown notification received:" << type.value;
235  }
236}
237
238void TabLoader::RemoveTab(NavigationController* tab) {
239  registrar_.Remove(this, NotificationType::TAB_CLOSED,
240                    Source<NavigationController>(tab));
241  registrar_.Remove(this, NotificationType::LOAD_STOP,
242                    Source<NavigationController>(tab));
243
244  TabsLoading::iterator i = tabs_loading_.find(tab);
245  if (i != tabs_loading_.end())
246    tabs_loading_.erase(i);
247
248  TabsToLoad::iterator j =
249      find(tabs_to_load_.begin(), tabs_to_load_.end(), tab);
250  if (j != tabs_to_load_.end())
251    tabs_to_load_.erase(j);
252}
253
254void TabLoader::ForceLoadTimerFired() {
255  force_load_delay_ *= 2;
256  LoadNextTab();
257}
258
259// SessionRestoreImpl ---------------------------------------------------------
260
261// SessionRestoreImpl is responsible for fetching the set of tabs to create
262// from SessionService. SessionRestoreImpl deletes itself when done.
263
264class SessionRestoreImpl : public NotificationObserver {
265 public:
266  SessionRestoreImpl(Profile* profile,
267                     Browser* browser,
268                     bool synchronous,
269                     bool clobber_existing_window,
270                     bool always_create_tabbed_browser,
271                     const std::vector<GURL>& urls_to_open)
272      : profile_(profile),
273        browser_(browser),
274        synchronous_(synchronous),
275        clobber_existing_window_(clobber_existing_window),
276        always_create_tabbed_browser_(always_create_tabbed_browser),
277        urls_to_open_(urls_to_open) {
278  }
279
280  void Restore() {
281    SessionService* session_service = profile_->GetSessionService();
282    DCHECK(session_service);
283    SessionService::SessionCallback* callback =
284        NewCallback(this, &SessionRestoreImpl::OnGotSession);
285    session_service->GetLastSession(&request_consumer_, callback);
286
287    if (synchronous_) {
288      bool old_state = MessageLoop::current()->NestableTasksAllowed();
289      MessageLoop::current()->SetNestableTasksAllowed(true);
290      MessageLoop::current()->Run();
291      MessageLoop::current()->SetNestableTasksAllowed(old_state);
292      ProcessSessionWindows(&windows_);
293      delete this;
294      return;
295    }
296
297    if (browser_) {
298      registrar_.Add(this, NotificationType::BROWSER_CLOSED,
299                     Source<Browser>(browser_));
300    }
301  }
302
303  void RestoreForeignSession(std::vector<SessionWindow*>* windows) {
304    tab_loader_.reset(new TabLoader());
305    // Create a browser instance to put the restored tabs in.
306    bool has_tabbed_browser = false;
307    for (std::vector<SessionWindow*>::iterator i = (*windows).begin();
308        i != (*windows).end(); ++i) {
309      Browser* browser = NULL;
310      if (!has_tabbed_browser && (*i)->type == Browser::TYPE_NORMAL)
311        has_tabbed_browser = true;
312      browser = new Browser(static_cast<Browser::Type>((*i)->type),
313          profile_);
314      browser->set_override_bounds((*i)->bounds);
315      browser->set_maximized_state((*i)->is_maximized ?
316          Browser::MAXIMIZED_STATE_MAXIMIZED :
317          Browser::MAXIMIZED_STATE_UNMAXIMIZED);
318      browser->CreateBrowserWindow();
319
320      // Restore and show the browser.
321      const int initial_tab_count = browser->tab_count();
322      RestoreTabsToBrowser(*(*i), browser);
323      ShowBrowser(browser, initial_tab_count,
324          (*i)->selected_tab_index);
325      NotifySessionServiceOfRestoredTabs(browser, initial_tab_count);
326    }
327    FinishedTabCreation(true, has_tabbed_browser);
328  }
329
330  ~SessionRestoreImpl() {
331    STLDeleteElements(&windows_);
332    restoring = false;
333  }
334
335  virtual void Observe(NotificationType type,
336                       const NotificationSource& source,
337                       const NotificationDetails& details) {
338    switch (type.value) {
339      case NotificationType::BROWSER_CLOSED:
340        delete this;
341        return;
342
343      default:
344        NOTREACHED();
345        break;
346    }
347  }
348
349 private:
350  // Invoked when done with creating all the tabs/browsers.
351  //
352  // |created_tabbed_browser| indicates whether a tabbed browser was created,
353  // or we used an existing tabbed browser.
354  //
355  // If successful, this begins loading tabs and deletes itself when all tabs
356  // have been loaded.
357  void FinishedTabCreation(bool succeeded, bool created_tabbed_browser) {
358    if (!created_tabbed_browser && always_create_tabbed_browser_) {
359      Browser* browser = Browser::Create(profile_);
360      if (urls_to_open_.empty()) {
361        // No tab browsers were created and no URLs were supplied on the command
362        // line. Add an empty URL, which is treated as opening the users home
363        // page.
364        urls_to_open_.push_back(GURL());
365      }
366      AppendURLsToBrowser(browser, urls_to_open_);
367      browser->window()->Show();
368    }
369
370    if (succeeded) {
371      DCHECK(tab_loader_.get());
372      // TabLoader delets itself when done loading.
373      tab_loader_.release()->StartLoading();
374    }
375
376    if (!synchronous_) {
377      // If we're not synchronous we need to delete ourself.
378      // NOTE: we must use DeleteLater here as most likely we're in a callback
379      // from the history service which doesn't deal well with deleting the
380      // object it is notifying.
381      MessageLoop::current()->DeleteSoon(FROM_HERE, this);
382    }
383  }
384
385  void OnGotSession(SessionService::Handle handle,
386                    std::vector<SessionWindow*>* windows) {
387    if (synchronous_) {
388      // See comment above windows_ as to why we don't process immediately.
389      windows_.swap(*windows);
390      MessageLoop::current()->Quit();
391      return;
392    }
393
394    ProcessSessionWindows(windows);
395  }
396
397  void ProcessSessionWindows(std::vector<SessionWindow*>* windows) {
398    if (windows->empty()) {
399      // Restore was unsuccessful.
400      FinishedTabCreation(false, false);
401      return;
402    }
403
404    tab_loader_.reset(new TabLoader());
405
406    Browser* current_browser =
407        browser_ ? browser_ : BrowserList::GetLastActive();
408    // After the for loop this contains the last TABBED_BROWSER. Is null if no
409    // tabbed browsers exist.
410    Browser* last_browser = NULL;
411    bool has_tabbed_browser = false;
412    for (std::vector<SessionWindow*>::iterator i = windows->begin();
413         i != windows->end(); ++i) {
414      Browser* browser = NULL;
415      if (!has_tabbed_browser && (*i)->type == Browser::TYPE_NORMAL)
416        has_tabbed_browser = true;
417      if (i == windows->begin() && (*i)->type == Browser::TYPE_NORMAL &&
418          !clobber_existing_window_) {
419        // If there is an open tabbed browser window, use it. Otherwise fall
420        // through and create a new one.
421        browser = current_browser;
422        if (browser && (browser->type() != Browser::TYPE_NORMAL ||
423                        browser->profile()->IsOffTheRecord())) {
424          browser = NULL;
425        }
426      }
427      if (!browser) {
428        browser = new Browser(static_cast<Browser::Type>((*i)->type), profile_);
429        browser->set_override_bounds((*i)->bounds);
430        browser->set_maximized_state((*i)->is_maximized ?
431            Browser::MAXIMIZED_STATE_MAXIMIZED :
432            Browser::MAXIMIZED_STATE_UNMAXIMIZED);
433        browser->CreateBrowserWindow();
434      }
435      if ((*i)->type == Browser::TYPE_NORMAL)
436        last_browser = browser;
437      const int initial_tab_count = browser->tab_count();
438      RestoreTabsToBrowser(*(*i), browser);
439      ShowBrowser(browser, initial_tab_count, (*i)->selected_tab_index);
440      NotifySessionServiceOfRestoredTabs(browser, initial_tab_count);
441    }
442
443    // If we're restoring a session as the result of a crash and the session
444    // included at least one tabbed browser, then close the browser window
445    // that was opened when the user clicked to restore the session.
446    if (clobber_existing_window_ && current_browser && has_tabbed_browser &&
447        current_browser->type() == Browser::TYPE_NORMAL) {
448      current_browser->CloseAllTabs();
449    }
450    if (last_browser && !urls_to_open_.empty())
451      AppendURLsToBrowser(last_browser, urls_to_open_);
452    // If last_browser is NULL and urls_to_open_ is non-empty,
453    // FinishedTabCreation will create a new TabbedBrowser and add the urls to
454    // it.
455    FinishedTabCreation(true, has_tabbed_browser);
456  }
457
458  void RestoreTabsToBrowser(const SessionWindow& window, Browser* browser) {
459    DCHECK(!window.tabs.empty());
460    for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin();
461         i != window.tabs.end(); ++i) {
462      const SessionTab& tab = *(*i);
463      DCHECK(!tab.navigations.empty());
464      int selected_index = tab.current_navigation_index;
465      selected_index = std::max(
466          0,
467          std::min(selected_index,
468                   static_cast<int>(tab.navigations.size() - 1)));
469      tab_loader_->ScheduleLoad(
470          &browser->AddRestoredTab(tab.navigations,
471                                   static_cast<int>(i - window.tabs.begin()),
472                                   selected_index,
473                                   tab.extension_app_id,
474                                   false,
475                                   tab.pinned,
476                                   true,
477                                   NULL)->controller());
478    }
479  }
480
481  void ShowBrowser(Browser* browser,
482                   int initial_tab_count,
483                   int selected_session_index) {
484    if (browser_ == browser) {
485      browser->SelectTabContentsAt(browser->tab_count() - 1, true);
486      return;
487    }
488
489    DCHECK(browser);
490    DCHECK(browser->tab_count());
491    browser->SelectTabContentsAt(
492        std::min(initial_tab_count + std::max(0, selected_session_index),
493                 browser->tab_count() - 1), true);
494    browser->window()->Show();
495    // TODO(jcampan): http://crbug.com/8123 we should not need to set the
496    //                initial focus explicitly.
497    browser->GetSelectedTabContents()->view()->SetInitialFocus();
498  }
499
500  // Appends the urls in |urls| to |browser|.
501  void AppendURLsToBrowser(Browser* browser,
502                           const std::vector<GURL>& urls) {
503    for (size_t i = 0; i < urls.size(); ++i) {
504      int add_types = TabStripModel::ADD_FORCE_INDEX;
505      if (i == 0)
506        add_types |= TabStripModel::ADD_SELECTED;
507      int index = browser->GetIndexForInsertionDuringRestore(i);
508      browser::NavigateParams params(browser, urls[i],
509                                     PageTransition::START_PAGE);
510      params.disposition = i == 0 ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
511      params.tabstrip_index = index;
512      params.tabstrip_add_types = add_types;
513      browser::Navigate(&params);
514    }
515  }
516
517  // Invokes TabRestored on the SessionService for all tabs in browser after
518  // initial_count.
519  void NotifySessionServiceOfRestoredTabs(Browser* browser, int initial_count) {
520    SessionService* session_service = profile_->GetSessionService();
521    for (int i = initial_count; i < browser->tab_count(); ++i)
522      session_service->TabRestored(&browser->GetTabContentsAt(i)->controller(),
523                                   browser->tabstrip_model()->IsTabPinned(i));
524  }
525
526  // The profile to create the sessions for.
527  Profile* profile_;
528
529  // The first browser to restore to, may be null.
530  Browser* browser_;
531
532  // Whether or not restore is synchronous.
533  const bool synchronous_;
534
535  // See description in RestoreSession (in .h).
536  const bool clobber_existing_window_;
537
538  // If true and there is an error or there are no windows to restore, we
539  // create a tabbed browser anyway. This is used on startup to make sure at
540  // at least one window is created.
541  const bool always_create_tabbed_browser_;
542
543  // Set of URLs to open in addition to those restored from the session.
544  std::vector<GURL> urls_to_open_;
545
546  // Used to get the session.
547  CancelableRequestConsumer request_consumer_;
548
549  // Responsible for loading the tabs.
550  scoped_ptr<TabLoader> tab_loader_;
551
552  // When synchronous we run a nested message loop. To avoid creating windows
553  // from the nested message loop (which can make exiting the nested message
554  // loop take a while) we cache the SessionWindows here and create the actual
555  // windows when the nested message loop exits.
556  std::vector<SessionWindow*> windows_;
557
558  NotificationRegistrar registrar_;
559};
560
561}  // namespace
562
563// SessionRestore -------------------------------------------------------------
564
565static void Restore(Profile* profile,
566                    Browser* browser,
567                    bool synchronous,
568                    bool clobber_existing_window,
569                    bool always_create_tabbed_browser,
570                    const std::vector<GURL>& urls_to_open) {
571#if defined(OS_CHROMEOS)
572  chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(
573      "SessionRestoreStarted", false);
574#endif
575  DCHECK(profile);
576  // Always restore from the original profile (incognito profiles have no
577  // session service).
578  profile = profile->GetOriginalProfile();
579  if (!profile->GetSessionService()) {
580    NOTREACHED();
581    return;
582  }
583  restoring = true;
584  profile->set_restored_last_session(true);
585  // SessionRestoreImpl takes care of deleting itself when done.
586  SessionRestoreImpl* restorer =
587      new SessionRestoreImpl(profile, browser, synchronous,
588                             clobber_existing_window,
589                             always_create_tabbed_browser, urls_to_open);
590  restorer->Restore();
591}
592
593// static
594void SessionRestore::RestoreSession(Profile* profile,
595                                    Browser* browser,
596                                    bool clobber_existing_window,
597                                    bool always_create_tabbed_browser,
598                                    const std::vector<GURL>& urls_to_open) {
599  Restore(profile, browser, false, clobber_existing_window,
600          always_create_tabbed_browser, urls_to_open);
601}
602
603// static
604void SessionRestore::RestoreForeignSessionWindows(Profile* profile,
605    std::vector<SessionWindow*>* windows) {
606  // Create a SessionRestore object to eventually restore the tabs.
607  std::vector<GURL> gurls;
608  SessionRestoreImpl restorer(profile,
609      static_cast<Browser*>(NULL), true, false, true, gurls);
610  restorer.RestoreForeignSession(windows);
611}
612
613// static
614void SessionRestore::RestoreSessionSynchronously(
615    Profile* profile,
616    const std::vector<GURL>& urls_to_open) {
617  Restore(profile, NULL, true, false, true, urls_to_open);
618}
619
620// static
621bool SessionRestore::IsRestoring() {
622  return restoring;
623}
624