Use TabLoader in TabRestoreService.

SessionService uses TabLoader to load tabs gradually.
The same behaviour is expected from TabRestoreService.

BUG=697510

Review-Url: https://codereview.chromium.org/2727663004
Cr-Commit-Position: refs/heads/master@{#454919}
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index ef4c3cd..753f4e04 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -31,6 +31,12 @@
 using content::RenderWidgetHost;
 using content::WebContents;
 
+namespace {
+
+size_t g_max_loaded_tab_count_for_testing = 0;
+
+}  // namespace
+
 void TabLoader::Observe(int type,
                         const content::NotificationSource& source,
                         const content::NotificationDetails& details) {
@@ -78,11 +84,17 @@
   shared_tab_loader_->StartLoading(tabs);
 }
 
+// static
+void TabLoader::SetMaxLoadedTabCountForTest(size_t value) {
+  g_max_loaded_tab_count_for_testing = value;
+}
+
 TabLoader::TabLoader(base::TimeTicks restore_started)
     : memory_pressure_listener_(
           base::Bind(&TabLoader::OnMemoryPressure, base::Unretained(this))),
       force_load_delay_multiplier_(1),
       loading_enabled_(true),
+      started_to_load_count_(0),
       restore_started_(restore_started) {
   stats_collector_ = new SessionRestoreStatsCollector(
       restore_started,
@@ -118,6 +130,7 @@
       if (favicon_driver)
         favicon_driver->FetchFavicon(favicon_driver->GetActiveURL());
     } else {
+      ++started_to_load_count_;
       tabs_loading_.insert(&restored_tab.contents()->GetController());
     }
     RegisterForNotifications(&restored_tab.contents()->GetController());
@@ -168,6 +181,7 @@
 
     NavigationController* controller = tabs_to_load_.front();
     DCHECK(controller);
+    ++started_to_load_count_;
     tabs_loading_.insert(controller);
     tabs_to_load_.pop_front();
     controller->LoadIfNecessary();
@@ -242,6 +256,9 @@
 }
 
 bool TabLoader::ShouldStopLoadingTabs() const {
+  if (g_max_loaded_tab_count_for_testing != 0 &&
+      started_to_load_count_ >= g_max_loaded_tab_count_for_testing)
+    return true;
   if (base::FeatureList::IsEnabled(features::kMemoryCoordinator))
     return base::MemoryCoordinatorProxy::GetInstance()->GetCurrentMemoryState()
         != base::MemoryState::NORMAL;
diff --git a/chrome/browser/sessions/tab_loader.h b/chrome/browser/sessions/tab_loader.h
index 81c6dd0..c3528f3 100644
--- a/chrome/browser/sessions/tab_loader.h
+++ b/chrome/browser/sessions/tab_loader.h
@@ -63,6 +63,8 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(TabLoaderTest, OnMemoryStateChange);
+  FRIEND_TEST_ALL_PREFIXES(TabRestoreTest,
+                           TabsFromRestoredWindowsAreLoadedGradually);
 
   friend class base::RefCounted<TabLoader>;
 
@@ -123,6 +125,11 @@
   // Stops loading tabs to purge memory by stopping to load any more tabs.
   void StopLoadingTabs();
 
+  // Limit the number of loaded tabs.
+  // Value of 0 restores default behavior. In test mode command line flags and
+  // free memory size are not taken into account.
+  static void SetMaxLoadedTabCountForTest(size_t value);
+
   std::unique_ptr<TabLoaderDelegate> delegate_;
 
   // Listens for system under memory pressure notifications and stops loading
@@ -144,6 +151,10 @@
   // The tabs we need to load.
   TabsToLoad tabs_to_load_;
 
+  // The number of tabs started to load.
+  // (This value never decreases.)
+  size_t started_to_load_count_;
+
   base::OneShotTimer force_load_timer_;
 
   // The time the restore process started.
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index b7e444d..70fc7a7 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -42,6 +42,10 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+#include "chrome/browser/sessions/tab_loader.h"
+#endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
+
 // Class used to run a message loop waiting for the TabRestoreService to finish
 // loading. Does nothing if the TabRestoreService was already loaded.
 class WaitForLoadObserver : public sessions::TabRestoreServiceObserver {
@@ -638,15 +642,16 @@
   EXPECT_EQ(initial_tab_count + 2, browser->tab_strip_model()->count());
   load_stop_observer.Wait();
 
+  EXPECT_EQ(initial_tab_count + 1, browser->tab_strip_model()->active_index());
   content::WebContents* restored_tab =
-      browser->tab_strip_model()->GetWebContentsAt(initial_tab_count);
-  EnsureTabFinishedRestoring(restored_tab);
-  EXPECT_EQ(url1_, restored_tab->GetURL());
-
-  restored_tab =
       browser->tab_strip_model()->GetWebContentsAt(initial_tab_count + 1);
   EnsureTabFinishedRestoring(restored_tab);
   EXPECT_EQ(url2_, restored_tab->GetURL());
+
+  restored_tab =
+      browser->tab_strip_model()->GetWebContentsAt(initial_tab_count);
+  EnsureTabFinishedRestoring(restored_tab);
+  EXPECT_EQ(url1_, restored_tab->GetURL());
 }
 
 // Restore tab with special URL chrome://credits/ and make sure the page loads
@@ -759,3 +764,54 @@
   Browser* browser = GetBrowser(0);
   EXPECT_EQ(4, browser->tab_strip_model()->count());
 }
+
+// TabLoader (used here) is available only when browser is built
+// with ENABLE_SESSION_SERVICE.
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+IN_PROC_BROWSER_TEST_F(TabRestoreTest,
+                       TabsFromRestoredWindowsAreLoadedGradually) {
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url2_, WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
+  Browser* browser2 = GetBrowser(1);
+
+  // Add tabs and close browser.
+  const int tabs_count = 4;
+  AddSomeTabs(browser2, tabs_count - browser2->tab_strip_model()->count());
+  EXPECT_EQ(tabs_count, browser2->tab_strip_model()->count());
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_BROWSER_CLOSED,
+      content::NotificationService::AllSources());
+  chrome::CloseWindow(browser2);
+  observer.Wait();
+
+  // Limit the number of restored tabs that are loaded.
+  TabLoader::SetMaxLoadedTabCountForTest(2);
+
+  // Restore recently closed window.
+  content::WindowedNotificationObserver open_window_observer(
+      chrome::NOTIFICATION_BROWSER_OPENED,
+      content::NotificationService::AllSources());
+  chrome::OpenWindowWithRestoredTabs(browser()->profile());
+  open_window_observer.Wait();
+  browser2 = GetBrowser(1);
+
+  EXPECT_EQ(tabs_count, browser2->tab_strip_model()->count());
+  EXPECT_EQ(tabs_count - 1, browser2->tab_strip_model()->active_index());
+  // These two tabs should be loaded by TabLoader.
+  EnsureTabFinishedRestoring(
+      browser2->tab_strip_model()->GetWebContentsAt(tabs_count - 1));
+  EnsureTabFinishedRestoring(browser2->tab_strip_model()->GetWebContentsAt(0));
+
+  // The following isn't necessary but just to be sure there is no any async
+  // task that could have an impact on the expectations below.
+  content::RunAllPendingInMessageLoop();
+
+  // These tabs shouldn't want to be loaded.
+  for (int tab_idx = 1; tab_idx < tabs_count - 1; ++tab_idx) {
+    auto* contents = browser2->tab_strip_model()->GetWebContentsAt(tab_idx);
+    EXPECT_FALSE(contents->IsLoading());
+    EXPECT_TRUE(contents->GetController().NeedsReload());
+  }
+}
+#endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
index 8b298ad..7374002 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
@@ -84,6 +84,7 @@
 
   // Create new tab.
   tab_model_->CreateTab(nullptr, web_contents, -1);
+  web_contents->GetController().LoadIfNecessary();
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
 
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index c6ee3f0..bf3672f 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -15,6 +15,10 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/session_storage_namespace.h"
 
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+#include "chrome/browser/sessions/tab_loader.h"
+#endif
+
 using content::NavigationController;
 using content::SessionStorageNamespace;
 using content::WebContents;
@@ -74,6 +78,24 @@
       browser_, navigations, tab_index, selected_navigation, extension_app_id,
       select, pin, from_last_session, storage_namespace, user_agent_override);
 
+#if BUILDFLAG(ENABLE_SESSION_SERVICE)
+  // The focused tab will be loaded by Browser, and TabLoader will load the
+  // rest.
+  if (!select) {
+    // Regression check: make sure that the tab hasn't started to load
+    // immediately.
+    DCHECK(web_contents->GetController().NeedsReload());
+    DCHECK(!web_contents->IsLoading());
+  }
+  std::vector<TabLoader::RestoredTab> restored_tabs;
+  restored_tabs.emplace_back(web_contents, select, !extension_app_id.empty(),
+                             pin);
+  TabLoader::RestoreTabs(restored_tabs, base::TimeTicks::Now());
+#else   // BUILDFLAG(ENABLE_SESSION_SERVICE)
+  // Load the tab manually if there is no TabLoader.
+  web_contents->GetController().LoadIfNecessary();
+#endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
+
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
 
diff --git a/chrome/browser/ui/browser_tabrestore_browsertest.cc b/chrome/browser/ui/browser_tabrestore_browsertest.cc
index 5fd6ac3..51acd8f94 100644
--- a/chrome/browser/ui/browser_tabrestore_browsertest.cc
+++ b/chrome/browser/ui/browser_tabrestore_browsertest.cc
@@ -13,6 +13,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "components/sessions/core/tab_restore_service.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
@@ -90,6 +91,24 @@
   EXPECT_EQ(2u, active_browser_list->size());
   browser = active_browser_list->get(1);
   EXPECT_EQ(3, browser->tab_strip_model()->count());
+  // For the two test tabs we've just received "READY" DOM message.
+  // But there won't be such message from the "about:blank" tab.
+  // And it is possible that TabLoader hasn't loaded it yet.
+  // Thus we should wait for "load stop" event before we will perform
+  // CheckVisbility on "about:blank".
+  {
+    const content::WebContents* about_blank_contents =
+        browser->tab_strip_model()->GetWebContentsAt(0);
+    EXPECT_EQ("about:blank", about_blank_contents->GetURL().spec());
+    if (about_blank_contents->IsLoading() ||
+        about_blank_contents->GetController().NeedsReload()) {
+      content::WindowedNotificationObserver load_stop_observer(
+          content::NOTIFICATION_LOAD_STOP,
+          content::Source<content::NavigationController>(
+              &about_blank_contents->GetController()));
+      load_stop_observer.Wait();
+    }
+  }
 
   // The middle tab only should have visible disposition.
   CheckVisbility(browser->tab_strip_model(), 1);
@@ -132,6 +151,21 @@
   EXPECT_EQ(2u, active_browser_list->size());
   browser = active_browser_list->get(1);
   EXPECT_EQ(3, browser->tab_strip_model()->count());
+  // The same as in RecentTabsMenuTabDisposition test case.
+  // See there for the explanation.
+  {
+    const content::WebContents* about_blank_contents =
+        browser->tab_strip_model()->GetWebContentsAt(0);
+    EXPECT_EQ("about:blank", about_blank_contents->GetURL().spec());
+    if (about_blank_contents->IsLoading() ||
+        about_blank_contents->GetController().NeedsReload()) {
+      content::WindowedNotificationObserver load_stop_observer(
+          content::NOTIFICATION_LOAD_STOP,
+          content::Source<content::NavigationController>(
+              &about_blank_contents->GetController()));
+      load_stop_observer.Wait();
+    }
+  }
 
   // The middle tab only should have visible disposition.
   CheckVisbility(browser->tab_strip_model(), 1);
diff --git a/components/sessions/content/content_live_tab.cc b/components/sessions/content/content_live_tab.cc
index 7b1dff0..3f1ef66 100644
--- a/components/sessions/content/content_live_tab.cc
+++ b/components/sessions/content/content_live_tab.cc
@@ -62,10 +62,6 @@
       web_contents());
 }
 
-void ContentLiveTab::LoadIfNecessary() {
-  navigation_controller().LoadIfNecessary();
-}
-
 const std::string& ContentLiveTab::GetUserAgentOverride() const {
   return web_contents()->GetUserAgentOverride();
 }
diff --git a/components/sessions/content/content_live_tab.h b/components/sessions/content/content_live_tab.h
index f4324411..5c04ba1 100644
--- a/components/sessions/content/content_live_tab.h
+++ b/components/sessions/content/content_live_tab.h
@@ -41,7 +41,6 @@
   int GetEntryCount() override;
   std::unique_ptr<PlatformSpecificTabData> GetPlatformSpecificTabData()
       override;
-  void LoadIfNecessary() override;
   const std::string& GetUserAgentOverride() const override;
 
   content::WebContents* web_contents() { return web_contents_; }
diff --git a/components/sessions/core/live_tab.h b/components/sessions/core/live_tab.h
index bd61293..4902199 100644
--- a/components/sessions/core/live_tab.h
+++ b/components/sessions/core/live_tab.h
@@ -32,10 +32,6 @@
   // implementation returns null.
   virtual std::unique_ptr<PlatformSpecificTabData> GetPlatformSpecificTabData();
 
-  // Loads the current page if necessary (where "necessary" is defined on a
-  // platform-specific basis).
-  virtual void LoadIfNecessary() = 0;
-
   // Returns the user agent override, if any.
   virtual const std::string& GetUserAgentOverride() const = 0;
 };
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index c140cda..f4f1119 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -209,7 +209,6 @@
               tab.from_last_session, tab.platform_data.get(),
               tab.user_agent_override);
           if (restored_tab) {
-            restored_tab->LoadIfNecessary();
             client_->OnTabRestored(
                 tab.navigations.at(tab.current_navigation_index).virtual_url());
             live_tabs.push_back(restored_tab);
@@ -482,7 +481,6 @@
         disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB, tab.pinned,
         tab.from_last_session, tab.platform_data.get(),
         tab.user_agent_override);
-    restored_tab->LoadIfNecessary();
   }
   client_->OnTabRestored(
       tab.navigations.at(tab.current_navigation_index).virtual_url());
diff --git a/components/sessions/ios/ios_live_tab.h b/components/sessions/ios/ios_live_tab.h
index d4363a51..17b6fe54 100644
--- a/components/sessions/ios/ios_live_tab.h
+++ b/components/sessions/ios/ios_live_tab.h
@@ -35,7 +35,6 @@
   sessions::SerializedNavigationEntry GetEntryAtIndex(int index) override;
   sessions::SerializedNavigationEntry GetPendingEntry() override;
   int GetEntryCount() override;
-  void LoadIfNecessary() override;
   const std::string& GetUserAgentOverride() const override;
 
   web::WebState* web_state() { return web_state_; }
diff --git a/components/sessions/ios/ios_live_tab.mm b/components/sessions/ios/ios_live_tab.mm
index 5f8f7dd..b34747a 100644
--- a/components/sessions/ios/ios_live_tab.mm
+++ b/components/sessions/ios/ios_live_tab.mm
@@ -54,10 +54,6 @@
   return navigation_manager()->GetItemCount();
 }
 
-void IOSLiveTab::LoadIfNecessary() {
-  navigation_manager()->LoadIfNecessary();
-}
-
 const std::string& IOSLiveTab::GetUserAgentOverride() const {
   // Dynamic user agent overrides are not supported on iOS.
   return user_agent_override_;