Commit 0bbb5a9c authored by Scott Haseley's avatar Scott Haseley Committed by Commit Bot

RC: Add tracking of UI tab count to TabLoadTracker

Tab helpers are attached to prerender web contents, and as a result
TabLoadTracker counts these web contentses as tabs. This CL adds
additional tracking of non-UI tabs and adds Get*TabUiCount() methods
that exclude non-UI tabs from counts.

This CL also modifies CoreTabHelperDelegates to notify TabLoadTracker
when tab contents are swapped. In that case, non-UI tabs are promoted to
UI tabs.

Bug: 861800
Change-Id: I3666b17634efd1445cd24cade8514fee06ad2c92
Reviewed-on: https://chromium-review.googlesource.com/1134504Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Commit-Queue: Scott Haseley <shaseley@google.com>
Cr-Commit-Position: refs/heads/master@{#577886}
parent e0a4d9cf
......@@ -33,6 +33,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/sessions/tab_restore_service_factory.h"
#include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
......@@ -343,6 +344,11 @@ std::unique_ptr<content::WebContents> TabAndroid::SwapTabContents(
std::unique_ptr<content::WebContents> new_contents,
bool did_start_load,
bool did_finish_load) {
// TODO(crbug.com/836409): TabLoadTracker should not rely on being notified
// directly about tab contents swaps.
resource_coordinator::TabLoadTracker::Get()->SwapTabContents(
old_contents, new_contents.get());
JNIEnv* env = base::android::AttachCurrentThread();
Java_Tab_swapWebContents(env, weak_java_tab_.get(env),
new_contents->GetJavaWebContents(), did_start_load,
......
......@@ -4,8 +4,11 @@
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include <utility>
#include "base/logging.h"
#include "base/stl_util.h"
#include "chrome/browser/ui/browser_tab_strip_tracker.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
......@@ -66,6 +69,33 @@ size_t TabLoadTracker::GetLoadedTabCount() const {
return state_counts_[static_cast<size_t>(LOADED)];
}
size_t TabLoadTracker::GetUiTabCount() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ui_tab_state_counts_[static_cast<size_t>(UNLOADED)] +
ui_tab_state_counts_[static_cast<size_t>(LOADING)] +
ui_tab_state_counts_[static_cast<size_t>(LOADED)];
}
size_t TabLoadTracker::GetUiTabCount(LoadingState loading_state) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ui_tab_state_counts_[static_cast<size_t>(loading_state)];
}
size_t TabLoadTracker::GetUnloadedUiTabCount() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ui_tab_state_counts_[static_cast<size_t>(UNLOADED)];
}
size_t TabLoadTracker::GetLoadingUiTabCount() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ui_tab_state_counts_[static_cast<size_t>(LOADING)];
}
size_t TabLoadTracker::GetLoadedUiTabCount() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ui_tab_state_counts_[static_cast<size_t>(LOADED)];
}
void TabLoadTracker::AddObserver(Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.AddObserver(observer);
......@@ -98,8 +128,11 @@ void TabLoadTracker::StartTracking(content::WebContents* web_contents) {
data.loading_state = loading_state;
if (data.loading_state == LOADING)
data.did_start_loading_seen = true;
data.is_ui_tab = IsUiTab(web_contents);
tabs_.insert(std::make_pair(web_contents, data));
++state_counts_[static_cast<size_t>(data.loading_state)];
if (data.is_ui_tab)
++ui_tab_state_counts_[static_cast<size_t>(data.loading_state)];
for (Observer& observer : observers_)
observer.OnStartTracking(web_contents, loading_state);
......@@ -113,6 +146,12 @@ void TabLoadTracker::StopTracking(content::WebContents* web_contents) {
auto loading_state = it->second.loading_state;
DCHECK_NE(0u, state_counts_[static_cast<size_t>(it->second.loading_state)]);
--state_counts_[static_cast<size_t>(it->second.loading_state)];
if (it->second.is_ui_tab) {
DCHECK_NE(
0u,
ui_tab_state_counts_[static_cast<size_t>(it->second.loading_state)]);
--ui_tab_state_counts_[static_cast<size_t>(it->second.loading_state)];
}
tabs_.erase(it);
for (Observer& observer : observers_)
......@@ -260,6 +299,11 @@ void TabLoadTracker::TransitionState(TabMap::iterator it,
--state_counts_[static_cast<size_t>(previous_state)];
it->second.loading_state = loading_state;
++state_counts_[static_cast<size_t>(loading_state)];
if (it->second.is_ui_tab) {
++ui_tab_state_counts_[static_cast<size_t>(loading_state)];
DCHECK_NE(0u, ui_tab_state_counts_[static_cast<size_t>(previous_state)]);
--ui_tab_state_counts_[static_cast<size_t>(previous_state)];
}
// If the destination state is LOADED, then also clear the
// |did_start_loading_seen| state.
......@@ -274,6 +318,53 @@ void TabLoadTracker::TransitionState(TabMap::iterator it,
observer.OnLoadingStateChange(web_contents, previous_state, loading_state);
}
bool TabLoadTracker::IsUiTab(content::WebContents* web_contents) {
// TODO(crbug.com/836409): This should be able to check directly with the
// tabstrip UI or use a platform-independent tabstrip observer interface to
// learn about |web_contents| associated with the tabstrip, rather than
// checking for specific cases where |web_contents| is not a ui tab.
if (prerender::PrerenderContents::FromWebContents(web_contents) != nullptr)
return false;
return true;
}
void TabLoadTracker::SwapTabContents(content::WebContents* old_contents,
content::WebContents* new_contents) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(crbug.com/836409): This should work by directly tracking tabs that are
// attached to UI surfaces instead of relying on being notified directly about
// tab contents swaps.
// Transition |old_contents| to a non-UI tab. If a tab is being swapped out,
// then it should exist, we should be tracking it, and it should be a UI tab.
DCHECK(old_contents);
auto it = tabs_.find(old_contents);
DCHECK(it != tabs_.end());
DCHECK(it->second.is_ui_tab);
it->second.is_ui_tab = false;
DCHECK_NE(
0u, ui_tab_state_counts_[static_cast<size_t>(it->second.loading_state)]);
--ui_tab_state_counts_[static_cast<size_t>(it->second.loading_state)];
// Transition |new_contents| to a UI tab.
DCHECK(IsUiTab(new_contents));
it = tabs_.find(new_contents);
// |new_contents| will not be tracked if a tab helper wasn't attached yet,
// which currently happens for dom distiller. In this case, the tab helper
// will be attached and we will start tracking it when it's swapped into the
// tab UI, which will happen later in this code path.
if (it == tabs_.end())
return;
// |new_contents| shouldn't be considered a UI tab yet. This should catch any
// new cases of non-tab web contents that attach tab helpers that we aren't
// handling.
DCHECK(!it->second.is_ui_tab);
// Promote |new_contents| to a UI tab.
it->second.is_ui_tab = true;
++ui_tab_state_counts_[static_cast<size_t>(it->second.loading_state)];
}
TabLoadTracker::Observer::Observer() {}
TabLoadTracker::Observer::~Observer() {}
......
......@@ -90,6 +90,18 @@ class TabLoadTracker {
size_t GetLoadingTabCount() const;
size_t GetLoadedTabCount() const;
// Returns the total number of UI tabs that are being tracked by this class.
// Some WebContents being tracked by this class may not yet be associated with
// a UI tab, e.g. prerender contents. To exclude these tabs from counts, use
// the Get*UiTabCount() methods.
size_t GetUiTabCount() const;
// Returns the number of UI tabs in each state.
size_t GetUiTabCount(LoadingState loading_state) const;
size_t GetUnloadedUiTabCount() const;
size_t GetLoadingUiTabCount() const;
size_t GetLoadedUiTabCount() const;
// Adds/removes an observer. It is up to the observer to ensure their lifetime
// exceeds that of the TabLoadTracker, as is removed prior to its destruction.
void AddObserver(Observer* observer);
......@@ -99,6 +111,11 @@ class TabLoadTracker {
void TransitionStateForTesting(content::WebContents* web_contents,
LoadingState loading_state);
// Called from CoreTabHelperDelegates when |new_contents| is replacing
// |old_contents| in a tab.
void SwapTabContents(content::WebContents* old_contents,
content::WebContents* new_contents);
protected:
// This allows the singleton constructor access to the protected constructor.
friend class base::NoDestructor<TabLoadTracker>;
......@@ -141,6 +158,14 @@ class TabLoadTracker {
// from the TabManager.
void OnPageAlmostIdle(content::WebContents* web_contents);
// Returns true if |web_contents| is a UI tab and false otherwise. This is
// used to filter out cases where tab helpers are attached to a non-UI tab
// WebContents, e.g prerender contents.
//
// This is virtual and protected for unittesting to control when web
// contentses are considered ui tabs.
virtual bool IsUiTab(content::WebContents* web_contents);
private:
// For unittesting.
friend class TestTabLoadTracker;
......@@ -149,6 +174,7 @@ class TabLoadTracker {
struct WebContentsData {
LoadingState loading_state = LoadingState::UNLOADED;
bool did_start_loading_seen = false;
bool is_ui_tab = false;
};
using TabMap = base::flat_map<content::WebContents*, WebContentsData>;
......@@ -168,12 +194,17 @@ class TabLoadTracker {
LoadingState loading_state,
bool validate_transition);
// The list of known WebContents and their states.
// The list of known WebContents and their states. This includes both UI and
// non-UI tabs.
TabMap tabs_;
// The counts of tabs in each state.
size_t state_counts_[static_cast<size_t>(LoadingState::kMaxValue) + 1] = {0};
// The counts of UI tabs in each state.
size_t ui_tab_state_counts_[static_cast<size_t>(LoadingState::kMaxValue) +
1] = {0};
base::ObserverList<Observer> observers_;
SEQUENCE_CHECKER(sequence_checker_);
......
......@@ -4,10 +4,18 @@
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include <memory>
#include <vector>
#include "base/process/kill.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/prerender/prerender_test_utils.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/web_contents_tester.h"
......@@ -35,7 +43,7 @@ class TestTabLoadTracker : public TabLoadTracker {
using TabLoadTracker::OnPageAlmostIdle;
using TabLoadTracker::DetermineLoadingState;
TestTabLoadTracker() {}
TestTabLoadTracker() : all_tabs_are_non_ui_tabs_(false) {}
virtual ~TestTabLoadTracker() {}
// Some accessors for TabLoadTracker internals.
......@@ -49,6 +57,19 @@ class TestTabLoadTracker : public TabLoadTracker {
return false;
return it->second.did_start_loading_seen;
}
bool IsUiTab(content::WebContents* web_contents) override {
if (all_tabs_are_non_ui_tabs_)
return false;
return TabLoadTracker::IsUiTab(web_contents);
}
void SetAllTabsAreNonUiTabs(bool enabled) {
all_tabs_are_non_ui_tabs_ = enabled;
}
private:
bool all_tabs_are_non_ui_tabs_;
};
// A mock observer class.
......@@ -143,7 +164,18 @@ class TabLoadTrackerTest : public ChromeRenderViewHostTestHarness {
EXPECT_EQ(loaded, tracker().GetLoadedTabCount());
}
void StateTransitionsTest(bool enable_pai);
void ExpectUiTabCounts(size_t tabs,
size_t unloaded,
size_t loading,
size_t loaded) {
EXPECT_EQ(tabs, unloaded + loading + loaded);
EXPECT_EQ(tabs, tracker().GetUiTabCount());
EXPECT_EQ(unloaded, tracker().GetUnloadedUiTabCount());
EXPECT_EQ(loading, tracker().GetLoadingUiTabCount());
EXPECT_EQ(loaded, tracker().GetLoadedUiTabCount());
}
void StateTransitionsTest(bool enable_pai, bool use_non_ui_tabs);
TestTabLoadTracker& tracker() { return tracker_; }
MockObserver& observer() { return observer_; }
......@@ -169,6 +201,17 @@ class TabLoadTrackerTest : public ChromeRenderViewHostTestHarness {
SCOPED_TRACE(""); \
ExpectTabCounts(a, b, c, d); \
}
#define EXPECT_UI_TAB_COUNTS(a, b, c, d) \
{ \
SCOPED_TRACE(""); \
ExpectUiTabCounts(a, b, c, d); \
}
#define EXPECT_TAB_AND_UI_TAB_COUNTS(a, b, c, d) \
{ \
SCOPED_TRACE(""); \
ExpectTabCounts(a, b, c, d); \
ExpectUiTabCounts(a, b, c, d); \
}
TEST_F(TabLoadTrackerTest, DetermineLoadingState) {
auto* tester1 = content::WebContentsTester::For(contents1());
......@@ -186,8 +229,10 @@ TEST_F(TabLoadTrackerTest, DetermineLoadingState) {
EXPECT_EQ(LoadingState::LOADED, tracker().DetermineLoadingState(contents1()));
}
void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai,
bool use_non_ui_tabs) {
SetPageAlmostIdleFeatureEnabled(enable_pai);
tracker().SetAllTabsAreNonUiTabs(use_non_ui_tabs);
auto* tester1 = content::WebContentsTester::For(contents1());
auto* tester2 = content::WebContentsTester::For(contents2());
......@@ -199,20 +244,35 @@ void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
tester3->NavigateAndCommit(GURL("http://bar.com"));
tester3->TestSetIsLoading(false);
// Add the contents to the trackers.
// Add the contents to the tracker.
EXPECT_CALL(observer(), OnStartTracking(contents1(), LoadingState::UNLOADED));
tracker().StartTracking(contents1());
EXPECT_TAB_COUNTS(1, 1, 0, 0);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(1, 1, 0, 0);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(1, 1, 0, 0);
}
testing::Mock::VerifyAndClearExpectations(&observer());
EXPECT_CALL(observer(), OnStartTracking(contents2(), LoadingState::LOADING));
tracker().StartTracking(contents2());
EXPECT_TAB_COUNTS(2, 1, 1, 0);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(2, 1, 1, 0);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 1, 1, 0);
}
testing::Mock::VerifyAndClearExpectations(&observer());
EXPECT_CALL(observer(), OnStartTracking(contents3(), LoadingState::LOADED));
tracker().StartTracking(contents3());
EXPECT_TAB_COUNTS(3, 1, 1, 1);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 1, 1, 1);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 1, 1, 1);
}
testing::Mock::VerifyAndClearExpectations(&observer());
// Start observers for the contents.
......@@ -230,10 +290,20 @@ void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
if (enable_pai) {
// The state transition should only occur *after* the PAI signal when that
// feature is enabled.
EXPECT_TAB_COUNTS(3, 1, 1, 1);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 1, 1, 1);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 1, 1, 1);
}
tracker().OnPageAlmostIdle(contents2());
}
EXPECT_TAB_COUNTS(3, 1, 0, 2);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 1, 0, 2);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 1, 0, 2);
}
testing::Mock::VerifyAndClearExpectations(&observer());
// Start the loading for contents1.
......@@ -241,7 +311,12 @@ void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
OnLoadingStateChange(contents1(), LoadingState::UNLOADED,
LoadingState::LOADING));
tester1->NavigateAndCommit(GURL("http://baz.com"));
EXPECT_TAB_COUNTS(3, 0, 1, 2);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 0, 1, 2);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 0, 1, 2);
}
testing::Mock::VerifyAndClearExpectations(&observer());
// Stop the loading with an error. The tab should go back to a LOADED
......@@ -251,7 +326,12 @@ void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
LoadingState::LOADED));
tester1->TestDidFailLoadWithError(GURL("http://baz.com"), 500,
base::UTF8ToUTF16("server error"));
ExpectTabCounts(3, 0, 0, 3);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 0, 0, 3);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 0, 0, 3);
}
testing::Mock::VerifyAndClearExpectations(&observer());
// Crash the render process corresponding to the main frame of a tab. This
......@@ -263,16 +343,163 @@ void TabLoadTrackerTest::StateTransitionsTest(bool enable_pai) {
static_cast<content::MockRenderProcessHost*>(
contents1()->GetMainFrame()->GetProcess());
rph->SimulateCrash();
ExpectTabCounts(3, 1, 0, 2);
if (use_non_ui_tabs) {
EXPECT_TAB_COUNTS(3, 1, 0, 2);
EXPECT_UI_TAB_COUNTS(0, 0, 0, 0);
} else {
EXPECT_TAB_AND_UI_TAB_COUNTS(3, 1, 0, 2);
}
testing::Mock::VerifyAndClearExpectations(&observer());
}
TEST_F(TabLoadTrackerTest, StateTransitions) {
StateTransitionsTest(false);
StateTransitionsTest(false /* enable_pai */, false /* use_non_ui_tabs */);
}
TEST_F(TabLoadTrackerTest, StateTransitionsPAI) {
StateTransitionsTest(true);
StateTransitionsTest(true /* enable_pai */, false /* use_non_ui_tabs */);
}
TEST_F(TabLoadTrackerTest, StateTransitionsNonUiTabs) {
StateTransitionsTest(false /* enable_pai */, true /* use_non_ui_tabs */);
}
TEST_F(TabLoadTrackerTest, StateTransitionsPAINonUiTabs) {
StateTransitionsTest(true /* enable_pai */, true /* use_non_ui_tabs */);
}
TEST_F(TabLoadTrackerTest, PrerenderContentsDoesNotChangeUiTabCounts) {
auto* tester1 = content::WebContentsTester::For(contents1());
tester1->NavigateAndCommit(GURL("http://baz.com"));
// Add the contents to the tracker.
EXPECT_CALL(observer(), OnStartTracking(contents1(), LoadingState::LOADING));
tracker().StartTracking(contents1());
EXPECT_TAB_AND_UI_TAB_COUNTS(1, 0, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
EXPECT_CALL(observer(), OnStartTracking(contents2(), LoadingState::UNLOADED));
tracker().StartTracking(contents2());
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 1, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
// Start observers for the contents.
TestWebContentsObserver observer1(contents1(), &tracker());
TestWebContentsObserver observer2(contents2(), &tracker());
// Prerender some contents.
prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
prerender::PrerenderManager::SetMode(
prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForBrowserContext(profile());
GURL url("http://www.example.com");
const gfx::Size kSize(640, 480);
std::unique_ptr<prerender::PrerenderHandle> prerender_handle(
prerender_manager->AddPrerenderFromOmnibox(
url, contents1()->GetController().GetDefaultSessionStorageNamespace(),
kSize));
const std::vector<content::WebContents*> contentses =
prerender_manager->GetAllPrerenderingContents();
ASSERT_EQ(1U, contentses.size());
// Prerendering should not change the UI tab counts, but should increase
// overall tab count. Note, contentses[0] is UNLOADED since it is not a test
// web contents and therefore hasn't started receiving data.
TestWebContentsObserver prerender_observer(contentses[0], &tracker());
EXPECT_CALL(observer(),
OnStartTracking(contentses[0], LoadingState::UNLOADED));
tracker().StartTracking(contentses[0]);
EXPECT_TAB_COUNTS(3, 2, 1, 0);
EXPECT_UI_TAB_COUNTS(2, 1, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
prerender_manager->CancelAllPrerenders();
}
TEST_F(TabLoadTrackerTest, SwapInUiTabContents) {
auto* tester1 = content::WebContentsTester::For(contents1());
tester1->NavigateAndCommit(GURL("http://baz.com"));
// Add the contents to the tracker.
EXPECT_CALL(observer(), OnStartTracking(contents1(), LoadingState::LOADING));
tracker().StartTracking(contents1());
EXPECT_TAB_AND_UI_TAB_COUNTS(1, 0, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
EXPECT_CALL(observer(), OnStartTracking(contents2(), LoadingState::UNLOADED));
tracker().StartTracking(contents2());
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 1, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
// Start observers for the contents.
TestWebContentsObserver observer1(contents1(), &tracker());
TestWebContentsObserver observer2(contents2(), &tracker());
// Simulate non-ui tab contents running in the background and getting swapped
// in. Non-ui tabs should not change the ui tab counts, but should change the
// overall tab counts.
std::unique_ptr<content::WebContents> non_ui_tab_contents =
CreateTestWebContents();
EXPECT_CALL(observer(), OnStartTracking(non_ui_tab_contents.get(),
LoadingState::UNLOADED));
tracker().SetAllTabsAreNonUiTabs(true);
tracker().StartTracking(non_ui_tab_contents.get());
EXPECT_TAB_COUNTS(3, 2, 1, 0);
EXPECT_UI_TAB_COUNTS(2, 1, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
// Swap in the prerender contents and simulate resulting tab strip swap.
// |non_ui_tab_contents| is already being tracked. The UI tab count should
// remain stable through the swap.
EXPECT_CALL(observer(), OnStopTracking(contents1(), LoadingState::LOADING));
tracker().SetAllTabsAreNonUiTabs(false);
tracker().SwapTabContents(contents1(), non_ui_tab_contents.get());
// After swap, but before we stop tracking the swapped-out contents. The UI
// tab counts should be in the end-state, but the total tab counts will be in
// the pre-swap state while the swapped-out contents is still being tracked.
EXPECT_TAB_COUNTS(3, 2, 1, 0);
EXPECT_UI_TAB_COUNTS(2, 2, 0, 0);
tracker().StopTracking(contents1());
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 2, 0, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
}
TEST_F(TabLoadTrackerTest, SwapInUntrackedContents) {
auto* tester1 = content::WebContentsTester::For(contents1());
tester1->NavigateAndCommit(GURL("http://baz.com"));
// Add the contents to the tracker.
EXPECT_CALL(observer(), OnStartTracking(contents1(), LoadingState::LOADING));
tracker().StartTracking(contents1());
EXPECT_TAB_AND_UI_TAB_COUNTS(1, 0, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
EXPECT_CALL(observer(), OnStartTracking(contents2(), LoadingState::UNLOADED));
tracker().StartTracking(contents2());
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 1, 1, 0);
testing::Mock::VerifyAndClearExpectations(&observer());
// Create an untracked web contents in the UNLOADED state, and swap it with
// the contents in the LOADING state. Since |untracked_contents| has no tab
// helper attached, swapping it in shouldn't changed the tab count.
std::unique_ptr<content::WebContents> untracked_contents =
CreateTestWebContents();
tracker().SwapTabContents(contents1(), untracked_contents.get());
// The total counts will remain stable since swapping out doesn't cause any
// web contents to stop being tracking. However, the swapped-out contents are
// no longer included in UI tab counts, and the swapped-in contents won't be
// until it is tracked.
EXPECT_TAB_COUNTS(2, 1, 1, 0);
EXPECT_UI_TAB_COUNTS(1, 1, 0, 0);
// Simulate swap in tab strip, which would cause |untracked_contents| to be
// tracked and the tab counts to change.
EXPECT_CALL(observer(), OnStopTracking(contents1(), LoadingState::LOADING));
EXPECT_CALL(observer(), OnStartTracking(untracked_contents.get(),
LoadingState::UNLOADED));
tracker().StopTracking(contents1());
tracker().StartTracking(untracked_contents.get());
EXPECT_TAB_AND_UI_TAB_COUNTS(2, 2, 0, 0);
}
} // namespace resource_coordinator
......@@ -78,6 +78,7 @@
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/repost_form_warning_controller.h"
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/sessions/session_restore.h"
......@@ -2003,6 +2004,11 @@ std::unique_ptr<content::WebContents> Browser::SwapTabContents(
new_view->TakeFallbackContentFrom(old_view);
}
// TODO(crbug.com/836409): TabLoadTracker should not rely on being notified
// directly about tab contents swaps.
resource_coordinator::TabLoadTracker::Get()->SwapTabContents(
old_contents, new_contents.get());
int index = tab_strip_model_->GetIndexOfWebContents(old_contents);
DCHECK_NE(TabStripModel::kNoTab, index);
return tab_strip_model_->ReplaceWebContentsAt(index, std::move(new_contents));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment