| Index: chrome/browser/sessions/session_restore.cc
|
| diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
|
| index 4ae68764e9d71a9bfb77dd3710b845616ad5acfb..79fc1cb1a9156966457ce41665be1a280d1e7928 100644
|
| --- a/chrome/browser/sessions/session_restore.cc
|
| +++ b/chrome/browser/sessions/session_restore.cc
|
| @@ -54,6 +54,9 @@ using content::WebContents;
|
| namespace {
|
|
|
| class SessionRestoreImpl;
|
| +class TabLoader;
|
| +
|
| +TabLoader* shared_tab_loader = NULL;
|
|
|
| // Pointers to SessionRestoreImpls which are currently restoring the session.
|
| std::set<SessionRestoreImpl*>* active_session_restorers = NULL;
|
| @@ -67,15 +70,23 @@ static const int kInitialDelayTimerMS = 100;
|
| // tabs. New tabs are loaded after the current tab finishes loading, or a delay
|
| // is reached (initially kInitialDelayTimerMS). If the delay is reached before
|
| // a tab finishes loading a new tab is loaded and the time of the delay
|
| -// doubled. When all tabs are loading TabLoader deletes itself.
|
| +// doubled.
|
| +//
|
| +// TabLoader keeps a reference to itself when it's loading. When it has finished
|
| +// loading, it drops the reference. If another profile is restored while the
|
| +// TabLoader is loading, it will schedule its tabs to get loaded by the same
|
| +// TabLoader. When doing the scheduling, it holds a reference to the TabLoader.
|
| //
|
| // This is not part of SessionRestoreImpl so that synchronous destruction
|
| // of SessionRestoreImpl doesn't have timing problems.
|
| class TabLoader : public content::NotificationObserver,
|
| - public net::NetworkChangeNotifier::OnlineStateObserver {
|
| + public net::NetworkChangeNotifier::OnlineStateObserver,
|
| + public base::RefCounted<TabLoader> {
|
| public:
|
| - explicit TabLoader(base::TimeTicks restore_started);
|
| - virtual ~TabLoader();
|
| + // Retrieves a pointer to the TabLoader instance shared between profiles, or
|
| + // creates a new TabLoader if it doesn't exist. If a TabLoader is created, its
|
| + // starting timestamp is set to |restore_started|.
|
| + static TabLoader* GetTabLoader(base::TimeTicks restore_started);
|
|
|
| // Schedules a tab for loading.
|
| void ScheduleLoad(NavigationController* controller);
|
| @@ -90,10 +101,15 @@ class TabLoader : public content::NotificationObserver,
|
| void StartLoading();
|
|
|
| private:
|
| + friend class base::RefCounted<TabLoader>;
|
| +
|
| typedef std::set<NavigationController*> TabsLoading;
|
| typedef std::list<NavigationController*> TabsToLoad;
|
| typedef std::set<RenderWidgetHost*> RenderWidgetHostSet;
|
|
|
| + explicit TabLoader(base::TimeTicks restore_started);
|
| + virtual ~TabLoader();
|
| +
|
| // Loads the next tab. If there are no more tabs to load this deletes itself,
|
| // otherwise |force_load_timer_| is restarted.
|
| void LoadNextTab();
|
| @@ -162,22 +178,18 @@ class TabLoader : public content::NotificationObserver,
|
| // Max number of tabs that were loaded in parallel (for metrics).
|
| size_t max_parallel_tab_loads_;
|
|
|
| + // For keeping TabLoader alive while it's loading even if no
|
| + // SessionRestoreImpls reference it.
|
| + scoped_refptr<TabLoader> this_retainer_;
|
| +
|
| DISALLOW_COPY_AND_ASSIGN(TabLoader);
|
| };
|
|
|
| -TabLoader::TabLoader(base::TimeTicks restore_started)
|
| - : force_load_delay_(kInitialDelayTimerMS),
|
| - loading_(false),
|
| - got_first_paint_(false),
|
| - tab_count_(0),
|
| - restore_started_(restore_started),
|
| - max_parallel_tab_loads_(0) {
|
| -}
|
| -
|
| -TabLoader::~TabLoader() {
|
| - DCHECK((got_first_paint_ || render_widget_hosts_to_paint_.empty()) &&
|
| - tabs_loading_.empty() && tabs_to_load_.empty());
|
| - net::NetworkChangeNotifier::RemoveOnlineStateObserver(this);
|
| +// static
|
| +TabLoader* TabLoader::GetTabLoader(base::TimeTicks restore_started) {
|
| + if (!shared_tab_loader)
|
| + shared_tab_loader = new TabLoader(restore_started);
|
| + return shared_tab_loader;
|
| }
|
|
|
| void TabLoader::ScheduleLoad(NavigationController* controller) {
|
| @@ -200,8 +212,15 @@ void TabLoader::TabIsLoading(NavigationController* controller) {
|
| }
|
|
|
| void TabLoader::StartLoading() {
|
| + // When multiple profiles are using the same TabLoader, another profile might
|
| + // already have started loading. In that case, the tabs scheduled for loading
|
| + // by this profile are already in the loading queue, and they will get loaded
|
| + // eventually.
|
| + if (loading_)
|
| + return;
|
| registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_PAINT,
|
| content::NotificationService::AllSources());
|
| + this_retainer_ = this;
|
| #if defined(OS_CHROMEOS)
|
| if (!net::NetworkChangeNotifier::IsOffline()) {
|
| loading_ = true;
|
| @@ -215,6 +234,22 @@ void TabLoader::StartLoading() {
|
| #endif
|
| }
|
|
|
| +TabLoader::TabLoader(base::TimeTicks restore_started)
|
| + : force_load_delay_(kInitialDelayTimerMS),
|
| + loading_(false),
|
| + got_first_paint_(false),
|
| + tab_count_(0),
|
| + restore_started_(restore_started),
|
| + max_parallel_tab_loads_(0) {
|
| +}
|
| +
|
| +TabLoader::~TabLoader() {
|
| + DCHECK((got_first_paint_ || render_widget_hosts_to_paint_.empty()) &&
|
| + tabs_loading_.empty() && tabs_to_load_.empty());
|
| + net::NetworkChangeNotifier::RemoveOnlineStateObserver(this);
|
| + shared_tab_loader = NULL;
|
| +}
|
| +
|
| void TabLoader::LoadNextTab() {
|
| if (!tabs_to_load_.empty()) {
|
| NavigationController* tab = tabs_to_load_.front();
|
| @@ -325,10 +360,13 @@ void TabLoader::Observe(int type,
|
| default:
|
| NOTREACHED() << "Unknown notification received:" << type;
|
| }
|
| - // Delete ourselves when we're not waiting for any more notifications.
|
| + // Delete ourselves when we're not waiting for any more notifications. If this
|
| + // was not the last reference, a SessionRestoreImpl holding a reference will
|
| + // eventually call StartLoading (which assigns this_retainer_), or drop the
|
| + // reference without initiating a load.
|
| if ((got_first_paint_ || render_widget_hosts_to_paint_.empty()) &&
|
| tabs_loading_.empty() && tabs_to_load_.empty())
|
| - delete this;
|
| + this_retainer_ = NULL;
|
| }
|
|
|
| void TabLoader::OnOnlineStateChanged(bool online) {
|
| @@ -596,7 +634,7 @@ class SessionRestoreImpl : public content::NotificationObserver {
|
| private:
|
| // Invoked when beginning to create new tabs. Resets the tab_loader_.
|
| void StartTabCreation() {
|
| - tab_loader_.reset(new TabLoader(restore_started_));
|
| + tab_loader_ = TabLoader::GetTabLoader(restore_started_);
|
| }
|
|
|
| // Invoked when done with creating all the tabs/browsers.
|
| @@ -625,7 +663,8 @@ class SessionRestoreImpl : public content::NotificationObserver {
|
| if (succeeded) {
|
| DCHECK(tab_loader_.get());
|
| // TabLoader deletes itself when done loading.
|
| - tab_loader_.release()->StartLoading();
|
| + tab_loader_->StartLoading();
|
| + tab_loader_ = NULL;
|
| }
|
|
|
| if (!synchronous_) {
|
| @@ -943,7 +982,7 @@ class SessionRestoreImpl : public content::NotificationObserver {
|
| CancelableRequestConsumer request_consumer_;
|
|
|
| // Responsible for loading the tabs.
|
| - scoped_ptr<TabLoader> tab_loader_;
|
| + scoped_refptr<TabLoader> tab_loader_;
|
|
|
| // When synchronous we run a nested message loop. To avoid creating windows
|
| // from the nested message loop (which can make exiting the nested message
|
|
|