blob: e352cc5a5c571ecaa34a95c5f913abe76d3c6b95 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
#include <memory>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "build/build_config.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/web_contents_tester.h"
#include "media/base/media_switches.h"
#include "media/base/test_data_util.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
namespace metrics {
namespace {
class TestTabStatsObserver : public TabStatsObserver {
public:
// Functions used to update the window/tab count.
void OnWindowAdded() override { ++window_count_; }
void OnWindowRemoved() override {
EXPECT_GT(window_count_, 0U);
--window_count_;
}
void OnTabAdded(content::WebContents* web_contents) override { ++tab_count_; }
void OnTabRemoved(content::WebContents* web_contents) override {
EXPECT_GT(tab_count_, 0U);
--tab_count_;
}
void OnTabInteraction(content::WebContents* web_contents) override {
++interaction_count_;
}
size_t tab_count() { return tab_count_; }
size_t window_count() { return window_count_; }
size_t interaction_count() { return interaction_count_; }
private:
size_t tab_count_ = 0;
size_t window_count_ = 0;
size_t interaction_count_ = 0;
};
using TabsStats = TabStatsDataStore::TabsStats;
using TabLifecycleObserver = resource_coordinator::TabLifecycleObserver;
void EnsureTabStatsMatchExpectations(const TabsStats& expected,
const TabsStats& actual) {
EXPECT_EQ(expected.total_tab_count, actual.total_tab_count);
EXPECT_EQ(expected.total_tab_count_max, actual.total_tab_count_max);
EXPECT_EQ(expected.max_tab_per_window, actual.max_tab_per_window);
EXPECT_EQ(expected.window_count, actual.window_count);
EXPECT_EQ(expected.window_count_max, actual.window_count_max);
}
} // namespace
class TabStatsTrackerBrowserTest : public InProcessBrowserTest {
public:
TabStatsTrackerBrowserTest() = default;
TabStatsTrackerBrowserTest(const TabStatsTrackerBrowserTest&) = delete;
TabStatsTrackerBrowserTest& operator=(const TabStatsTrackerBrowserTest&) =
delete;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(
switches::kAutoplayPolicy,
switches::autoplay::kNoUserGestureRequiredPolicy);
}
void SetUpOnMainThread() override {
tab_stats_tracker_ = TabStatsTracker::GetInstance();
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
protected:
// Used to make sure that the metrics are reported properly.
base::HistogramTester histogram_tester_;
raw_ptr<TabStatsTracker> tab_stats_tracker_{nullptr};
std::vector<std::unique_ptr<TestTabStatsObserver>> test_tab_stats_observers_;
};
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TabsAndWindowsAreCountedAccurately) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
TabsStats expected_stats = {};
// There should be only one window with one tab at startup.
expected_stats.total_tab_count = 1;
expected_stats.total_tab_count_max = 1;
expected_stats.max_tab_per_window = 1;
expected_stats.window_count = 1;
expected_stats.window_count_max = 1;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
// Add a tab and make sure that the counters get updated.
ASSERT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
++expected_stats.total_tab_count;
++expected_stats.total_tab_count_max;
++expected_stats.max_tab_per_window;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
--expected_stats.total_tab_count;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
Browser* new_browser = CreateBrowser(browser()->profile());
++expected_stats.total_tab_count;
++expected_stats.window_count;
++expected_stats.window_count_max;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
ASSERT_TRUE(AddTabAtIndexToBrowser(new_browser, 1, GURL("about:blank"),
ui::PAGE_TRANSITION_TYPED, true));
++expected_stats.total_tab_count;
++expected_stats.total_tab_count_max;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
CloseBrowserSynchronously(new_browser);
expected_stats.total_tab_count = 1;
expected_stats.window_count = 1;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
}
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
AdditionalTabStatsObserverGetsInitiliazed) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
TabsStats expected_stats = {};
// There should be only one window with one tab at startup.
expected_stats.total_tab_count = 1;
expected_stats.total_tab_count_max = 1;
expected_stats.max_tab_per_window = 1;
expected_stats.window_count = 1;
expected_stats.window_count_max = 1;
EnsureTabStatsMatchExpectations(expected_stats,
tab_stats_tracker_->tab_stats());
test_tab_stats_observers_.push_back(std::make_unique<TestTabStatsObserver>());
TestTabStatsObserver* first_observer = test_tab_stats_observers_.back().get();
tab_stats_tracker_->AddObserverAndSetInitialState(first_observer);
// Observer is initialized properly.
DCHECK_EQ(first_observer->tab_count(), expected_stats.total_tab_count);
DCHECK_EQ(first_observer->window_count(), expected_stats.window_count);
// Add some tabs and windows to increase the counts.
Browser* new_browser = CreateBrowser(browser()->profile());
++expected_stats.total_tab_count;
++expected_stats.window_count;
ASSERT_TRUE(AddTabAtIndexToBrowser(new_browser, 1, GURL("about:blank"),
ui::PAGE_TRANSITION_TYPED, true));
++expected_stats.total_tab_count;
test_tab_stats_observers_.push_back(std::make_unique<TestTabStatsObserver>());
TestTabStatsObserver* second_observer =
test_tab_stats_observers_.back().get();
tab_stats_tracker_->AddObserverAndSetInitialState(second_observer);
// Observer is initialized properly.
DCHECK_EQ(second_observer->tab_count(), expected_stats.total_tab_count);
DCHECK_EQ(second_observer->window_count(), expected_stats.window_count);
}
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
TabDeletionGetsHandledProperly) {
// Assert that the |TabStatsTracker| instance is initialized during the
// creation of the main browser.
ASSERT_TRUE(tab_stats_tracker_ != nullptr);
constexpr base::TimeDelta kValidLongInterval = base::Hours(12);
TabStatsDataStore* data_store = tab_stats_tracker_->tab_stats_data_store();
TabStatsDataStore::TabsStateDuringIntervalMap* interval_map =
data_store->AddInterval();
ASSERT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
EXPECT_EQ(2U, interval_map->size());
content::WebContents* web_contents =
data_store->existing_tabs_for_testing()->begin()->first;
// Delete one of the WebContents without calling the |OnTabRemoved| function,
// the WebContentsObserver owned by |tab_stats_tracker_| should be notified
// and this should be handled correctly.
TabStatsDataStore::TabID tab_id =
data_store->GetTabIDForTesting(web_contents).value();
browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
EXPECT_TRUE(base::Contains(*interval_map, tab_id));
tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map);
EXPECT_EQ(1U, interval_map->size());
EXPECT_FALSE(base::Contains(*interval_map, tab_id));
web_contents = data_store->existing_tabs_for_testing()->begin()->first;
// Do this a second time, ensures that the situation where there's no existing
// tabs is handled properly.
tab_id = data_store->GetTabIDForTesting(web_contents).value();
browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
EXPECT_TRUE(base::Contains(*interval_map, tab_id));
tab_stats_tracker_->OnInterval(kValidLongInterval, interval_map);
EXPECT_EQ(0U, interval_map->size());
}
namespace {
class LenientMockTabStatsObserver : public TabStatsObserver {
public:
LenientMockTabStatsObserver() = default;
~LenientMockTabStatsObserver() override = default;
LenientMockTabStatsObserver(const LenientMockTabStatsObserver& other) =
delete;
LenientMockTabStatsObserver& operator=(const LenientMockTabStatsObserver&) =
delete;
MOCK_METHOD0(OnWindowAdded, void());
MOCK_METHOD0(OnWindowRemoved, void());
MOCK_METHOD1(OnTabAdded, void(content::WebContents*));
MOCK_METHOD1(OnTabRemoved, void(content::WebContents*));
MOCK_METHOD2(OnTabReplaced,
void(content::WebContents*, content::WebContents*));
MOCK_METHOD1(OnPrimaryMainFrameNavigationCommitted,
void(content::WebContents*));
MOCK_METHOD1(OnTabInteraction, void(content::WebContents*));
MOCK_METHOD1(OnTabIsAudibleChanged, void(content::WebContents*));
MOCK_METHOD1(OnTabVisibilityChanged, void(content::WebContents*));
MOCK_METHOD2(OnMediaEffectivelyFullscreenChanged,
void(content::WebContents*, bool));
};
using MockTabStatsObserver = testing::NiceMock<LenientMockTabStatsObserver>;
} // namespace
// TODO(1183746): Fix the flakiness on MacOS and re-enable the test.
#if BUILDFLAG(IS_MAC)
#define MAYBE_TabStatsObserverBasics DISABLED_TabStatsObserverBasics
#else
#define MAYBE_TabStatsObserverBasics TabStatsObserverBasics
#endif
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
MAYBE_TabStatsObserverBasics) {
MockTabStatsObserver mock_observer;
TestTabStatsObserver count_observer;
tab_stats_tracker_->AddObserverAndSetInitialState(&count_observer);
auto* window1_tab1 = browser()->tab_strip_model()->GetWebContentsAt(0);
EXPECT_EQ(content::Visibility::VISIBLE, window1_tab1->GetVisibility());
// The browser starts with one window and one visible tab, the observer will
// be notified immediately about those.
EXPECT_CALL(mock_observer, OnWindowAdded());
EXPECT_CALL(mock_observer, OnTabAdded(window1_tab1));
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
::testing::Mock::VerifyAndClear(&mock_observer);
// Mark the tab as hidden.
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab1));
window1_tab1->WasHidden();
::testing::Mock::VerifyAndClear(&mock_observer);
// Make it visible again.
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab1));
window1_tab1->WasShown();
::testing::Mock::VerifyAndClear(&mock_observer);
// Create a second browser window. This will cause one visible tab to be
// created and its main frame will do a navigation.
EXPECT_CALL(mock_observer, OnWindowAdded());
EXPECT_CALL(mock_observer, OnTabAdded(::testing::_));
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(::testing::_));
Browser* window2 = CreateBrowser(browser()->profile());
::testing::Mock::VerifyAndClear(&mock_observer);
// Make sure that the 2 windows don't overlap to avoid some unexpected
// visibility change events because one tab occludes the other.
// This resizes the two windows so they're right next to each other.
const gfx::NativeWindow window = browser()->window()->GetNativeWindow();
gfx::Rect work_area =
display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
const gfx::Size size(work_area.width() / 3, work_area.height() / 2);
gfx::Rect browser_rect(work_area.origin(), size);
browser()->window()->SetBounds(browser_rect);
browser_rect.set_x(browser_rect.right());
window2->window()->SetBounds(browser_rect);
auto* window2_tab1 = window2->tab_strip_model()->GetWebContentsAt(0);
// Adding a tab to the second window will cause its previous frame to become
// hidden.
EXPECT_CALL(mock_observer, OnTabAdded(::testing::_));
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window2_tab1));
ASSERT_TRUE(AddTabAtIndexToBrowser(window2, 1, GURL("about:blank"),
ui::PAGE_TRANSITION_TYPED, true));
::testing::Mock::VerifyAndClear(&mock_observer);
auto* window2_tab2 = window2->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(content::Visibility::HIDDEN, window2_tab1->GetVisibility());
EXPECT_EQ(content::Visibility::VISIBLE, window2_tab2->GetVisibility());
// Make sure that the visibility change events are properly forwarded.
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window2_tab2));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window2_tab1));
window2->tab_strip_model()->ActivateTabAt(0);
::testing::Mock::VerifyAndClear(&mock_observer);
EXPECT_EQ(content::Visibility::VISIBLE, window2_tab1->GetVisibility());
EXPECT_EQ(content::Visibility::HIDDEN, window2_tab2->GetVisibility());
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window2_tab1));
EXPECT_CALL(mock_observer, OnTabRemoved(window2_tab1));
EXPECT_CALL(mock_observer, OnTabRemoved(window2_tab2));
EXPECT_CALL(mock_observer, OnWindowRemoved());
CloseBrowserSynchronously(window2);
::testing::Mock::VerifyAndClear(&mock_observer);
EXPECT_EQ(content::Visibility::VISIBLE, window1_tab1->GetVisibility());
EXPECT_CALL(mock_observer, OnTabRemoved(window1_tab1));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab1));
EXPECT_CALL(mock_observer, OnWindowRemoved());
CloseBrowserSynchronously(browser());
::testing::Mock::VerifyAndClear(&mock_observer);
tab_stats_tracker_->RemoveObserver(&mock_observer);
tab_stats_tracker_->RemoveObserver(&count_observer);
EXPECT_EQ(0U, count_observer.tab_count());
EXPECT_EQ(0U, count_observer.window_count());
}
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest, TabSwitch) {
MockTabStatsObserver mock_observer;
TestTabStatsObserver count_observer;
tab_stats_tracker_->AddObserverAndSetInitialState(&count_observer);
auto* window1_tab1 = browser()->tab_strip_model()->GetWebContentsAt(0);
EXPECT_EQ(content::Visibility::VISIBLE, window1_tab1->GetVisibility());
// The browser starts with one window and one visible tab, the observer will
// be notified immediately about those.
EXPECT_CALL(mock_observer, OnWindowAdded());
EXPECT_CALL(mock_observer, OnTabAdded(::testing::_));
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
::testing::Mock::VerifyAndClear(&mock_observer);
EXPECT_CALL(mock_observer, OnTabAdded(::testing::_));
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab1));
ASSERT_TRUE(AddTabAtIndexToBrowser(browser(), 1, GURL("about:blank"),
ui::PAGE_TRANSITION_TYPED, true));
::testing::Mock::VerifyAndClear(&mock_observer);
EXPECT_EQ(content::Visibility::HIDDEN, window1_tab1->GetVisibility());
auto* window1_tab2 = browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(content::Visibility::VISIBLE, window1_tab2->GetVisibility());
// A tab switch should cause 2 visibility change events. The "tab hidden"
// notification should arrive before the "tab visible" one.
{
::testing::InSequence s;
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab2));
EXPECT_CALL(mock_observer, OnTabVisibilityChanged(window1_tab1));
browser()->tab_strip_model()->ActivateTabAt(0);
}
tab_stats_tracker_->RemoveObserver(&mock_observer);
tab_stats_tracker_->RemoveObserver(&count_observer);
EXPECT_EQ(2U, count_observer.tab_count());
EXPECT_EQ(1U, count_observer.window_count());
}
namespace {
// Observes a WebContents and waits until it becomes audible.
// both indicate that they are audible.
class AudioStartObserver : public content::WebContentsObserver {
public:
AudioStartObserver(content::WebContents* web_contents,
base::OnceClosure quit_closure)
: content::WebContentsObserver(web_contents),
quit_closure_(std::move(quit_closure)) {
DCHECK(!web_contents->IsCurrentlyAudible());
}
~AudioStartObserver() override = default;
// WebContentsObserver:
void OnAudioStateChanged(bool audible) override {
DCHECK(audible);
std::move(quit_closure_).Run();
}
private:
base::OnceClosure quit_closure_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest, AddObserverAudibleTab) {
// Set up the embedded test server to serve the test javascript file.
embedded_test_server()->ServeFilesFromSourceDirectory(
media::GetTestDataPath());
ASSERT_TRUE(embedded_test_server()->Start());
// Open the test JS file in the only WebContents.
auto* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
ASSERT_TRUE(NavigateToURL(web_contents, embedded_test_server()->GetURL(
"/webaudio_oscillator.html")));
// Start the audio.
base::RunLoop run_loop;
AudioStartObserver audio_start_observer(web_contents, run_loop.QuitClosure());
EXPECT_EQ("OK", content::EvalJs(web_contents, "StartOscillator();"));
run_loop.Run();
// Adding an observer now should receive the OnTabIsAudibleChanged() call.
MockTabStatsObserver mock_observer;
EXPECT_CALL(mock_observer, OnWindowAdded());
EXPECT_CALL(mock_observer, OnTabAdded(web_contents));
EXPECT_CALL(mock_observer, OnTabIsAudibleChanged(web_contents));
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
// Clean up.
tab_stats_tracker_->RemoveObserver(&mock_observer);
}
class TabStatsTrackerPrerenderBrowserTest : public TabStatsTrackerBrowserTest {
public:
TabStatsTrackerPrerenderBrowserTest()
: prerender_helper_(base::BindRepeating(
&TabStatsTrackerPrerenderBrowserTest::GetWebContents,
base::Unretained(this))) {}
~TabStatsTrackerPrerenderBrowserTest() override = default;
TabStatsTrackerPrerenderBrowserTest(
const TabStatsTrackerPrerenderBrowserTest&) = delete;
TabStatsTrackerPrerenderBrowserTest& operator=(
const TabStatsTrackerPrerenderBrowserTest&) = delete;
void SetUp() override {
prerender_helper_.SetUp(embedded_test_server());
TabStatsTrackerBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
TabStatsTrackerBrowserTest::SetUpOnMainThread();
}
content::test::PrerenderTestHelper& prerender_test_helper() {
return prerender_helper_;
}
private:
content::test::PrerenderTestHelper prerender_helper_;
};
IN_PROC_BROWSER_TEST_F(
TabStatsTrackerPrerenderBrowserTest,
PrerenderingShouldNotCallOnPrimaryMainFrameNavigationCommitted) {
GURL initial_url = embedded_test_server()->GetURL("/empty.html");
GURL prerender_url = embedded_test_server()->GetURL("/title1.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
std::unique_ptr<content::test::PrerenderHostObserver> host_observer;
// OnPrimaryMainFrameNavigationCommitted() should not be called in
// prerendering.
{
MockTabStatsObserver mock_observer;
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_))
.Times(0);
int host_id = prerender_test_helper().AddPrerender(prerender_url);
host_observer = std::make_unique<content::test::PrerenderHostObserver>(
*GetWebContents(), host_id);
EXPECT_FALSE(host_observer->was_activated());
tab_stats_tracker_->RemoveObserver(&mock_observer);
}
// OnPrimaryMainFrameNavigationCommitted() should be called after activating.
{
MockTabStatsObserver mock_observer;
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_))
.Times(1);
prerender_test_helper().NavigatePrimaryPage(prerender_url);
EXPECT_TRUE(host_observer->was_activated());
tab_stats_tracker_->RemoveObserver(&mock_observer);
}
}
class TabStatsTrackerSubFrameBrowserTest : public TabStatsTrackerBrowserTest {
public:
TabStatsTrackerSubFrameBrowserTest() = default;
~TabStatsTrackerSubFrameBrowserTest() override = default;
TabStatsTrackerSubFrameBrowserTest(
const TabStatsTrackerSubFrameBrowserTest&) = delete;
TabStatsTrackerSubFrameBrowserTest& operator=(
const TabStatsTrackerSubFrameBrowserTest&) = delete;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
TabStatsTrackerBrowserTest::SetUpOnMainThread();
}
protected:
content::test::FencedFrameTestHelper fenced_frame_helper_;
};
// Ensure that subframe navigation cannot affect TabStatsTracker.
IN_PROC_BROWSER_TEST_F(TabStatsTrackerSubFrameBrowserTest,
VerifyBehaviorOnSubFrameNavigation) {
MockTabStatsObserver mock_observer;
TestTabStatsObserver count_observer;
tab_stats_tracker_->AddObserverAndSetInitialState(&mock_observer);
tab_stats_tracker_->AddObserverAndSetInitialState(&count_observer);
// Navigate to an initial page.
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_))
.Times(1);
const GURL initial_url = embedded_test_server()->GetURL("/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
EXPECT_EQ(1U, count_observer.interaction_count());
::testing::Mock::VerifyAndClear(&mock_observer);
// Create an iframe and navigate inside the iframe.
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_))
.Times(0);
ASSERT_TRUE(content::ExecJs(
GetWebContents(),
content::JsReplace("var iframe = document.createElement('iframe');"
"iframe.src = $1;"
"document.body.appendChild(iframe);",
embedded_test_server()->GetURL("/title1.html"))));
WaitForLoadStop(GetWebContents());
::testing::Mock::VerifyAndClear(&mock_observer);
// Create a fenced frame and navigate inside the fenced frame.
EXPECT_CALL(mock_observer,
OnPrimaryMainFrameNavigationCommitted(::testing::_))
.Times(0);
content::RenderFrameHost* fenced_frame_host =
fenced_frame_helper_.CreateFencedFrame(
GetWebContents()->GetPrimaryMainFrame(),
embedded_test_server()->GetURL("/fenced_frames/title1.html"));
ASSERT_NE(nullptr, fenced_frame_host);
::testing::Mock::VerifyAndClear(&mock_observer);
tab_stats_tracker_->RemoveObserver(&mock_observer);
tab_stats_tracker_->RemoveObserver(&count_observer);
// Ensure that subframe navigation doesn't increase the user interaction
// count.
EXPECT_EQ(1U, count_observer.interaction_count());
}
} // namespace metrics