Commit 4d08cfa6 authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

Add resource_coordinator::TabLifecycleUnitSource.

Creates / destroys TabLifecycleUnits and notifies observers as tabs are
created / destroyed.

Bug: 775644
Change-Id: I8042e656c2672dbc71ad0187c151140a098f1356
Reviewed-on: https://chromium-review.googlesource.com/789410
Commit-Queue: François Doray <fdoray@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523844}
parent 99554ae8
......@@ -2579,6 +2579,8 @@ split_static_library("browser") {
"resource_coordinator/tab_lifecycle_unit.h",
"resource_coordinator/tab_lifecycle_unit_external.cc",
"resource_coordinator/tab_lifecycle_unit_external.h",
"resource_coordinator/tab_lifecycle_unit_source.cc",
"resource_coordinator/tab_lifecycle_unit_source.h",
"resource_coordinator/tab_manager.cc",
"resource_coordinator/tab_manager.h",
"resource_coordinator/tab_manager_delegate_chromeos.cc",
......
......@@ -14,7 +14,7 @@
namespace resource_coordinator {
TabLifecycleUnit::TabLifecycleUnit(
TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit(
base::ObserverList<TabLifecycleObserver>* observers,
content::WebContents* web_contents,
TabStripModel* tab_strip_model)
......@@ -26,19 +26,21 @@ TabLifecycleUnit::TabLifecycleUnit(
DCHECK(tab_strip_model_);
}
TabLifecycleUnit::~TabLifecycleUnit() = default;
TabLifecycleUnitSource::TabLifecycleUnit::~TabLifecycleUnit() = default;
void TabLifecycleUnit::SetTabStripModel(TabStripModel* tab_strip_model) {
void TabLifecycleUnitSource::TabLifecycleUnit::SetTabStripModel(
TabStripModel* tab_strip_model) {
DCHECK(tab_strip_model);
tab_strip_model = tab_strip_model_;
}
void TabLifecycleUnit::SetWebContents(content::WebContents* web_contents) {
void TabLifecycleUnitSource::TabLifecycleUnit::SetWebContents(
content::WebContents* web_contents) {
DCHECK(web_contents);
web_contents_ = web_contents;
}
void TabLifecycleUnit::SetFocused(bool focused) {
void TabLifecycleUnitSource::TabLifecycleUnit::SetFocused(bool focused) {
const bool was_focused = last_focused_time_ == base::TimeTicks::Max();
if (focused == was_focused)
return;
......@@ -47,11 +49,11 @@ void TabLifecycleUnit::SetFocused(bool focused) {
// TODO(fdoray): Reload the tab if discarded. https://crbug.com/775644
}
base::string16 TabLifecycleUnit::GetTitle() const {
base::string16 TabLifecycleUnitSource::TabLifecycleUnit::GetTitle() const {
return GetWebContents()->GetTitle();
}
std::string TabLifecycleUnit::GetIconURL() const {
std::string TabLifecycleUnitSource::TabLifecycleUnit::GetIconURL() const {
auto* last_committed_entry =
GetWebContents()->GetController().GetLastCommittedEntry();
if (!last_committed_entry)
......@@ -60,38 +62,45 @@ std::string TabLifecycleUnit::GetIconURL() const {
return favicon.valid ? favicon.url.spec() : std::string();
}
LifecycleUnit::SortKey TabLifecycleUnit::GetSortKey() const {
LifecycleUnit::SortKey TabLifecycleUnitSource::TabLifecycleUnit::GetSortKey()
const {
return SortKey(last_focused_time_);
}
LifecycleUnit::State TabLifecycleUnit::GetState() const {
LifecycleUnit::State TabLifecycleUnitSource::TabLifecycleUnit::GetState()
const {
return state_;
}
int TabLifecycleUnit::GetEstimatedMemoryFreedOnDiscardKB() const {
int TabLifecycleUnitSource::TabLifecycleUnit::
GetEstimatedMemoryFreedOnDiscardKB() const {
// TODO(fdoray): Implement this. https://crbug.com/775644
return 0;
}
bool TabLifecycleUnit::CanDiscard(DiscardReason reason) const {
bool TabLifecycleUnitSource::TabLifecycleUnit::CanDiscard(
DiscardReason reason) const {
// TODO(fdoray): Implement this. https://crbug.com/775644
return false;
}
bool TabLifecycleUnit::Discard(DiscardReason discard_reason) {
bool TabLifecycleUnitSource::TabLifecycleUnit::Discard(
DiscardReason discard_reason) {
// TODO(fdoray): Implement this. https://crbug.com/775644
return false;
}
content::WebContents* TabLifecycleUnit::GetWebContents() const {
content::WebContents* TabLifecycleUnitSource::TabLifecycleUnit::GetWebContents()
const {
return web_contents_;
}
bool TabLifecycleUnit::IsAutoDiscardable() const {
bool TabLifecycleUnitSource::TabLifecycleUnit::IsAutoDiscardable() const {
return auto_discardable_;
}
void TabLifecycleUnit::SetAutoDiscardable(bool auto_discardable) {
void TabLifecycleUnitSource::TabLifecycleUnit::SetAutoDiscardable(
bool auto_discardable) {
if (auto_discardable_ == auto_discardable)
return;
auto_discardable_ = auto_discardable;
......@@ -99,11 +108,11 @@ void TabLifecycleUnit::SetAutoDiscardable(bool auto_discardable) {
observer.OnAutoDiscardableStateChange(GetWebContents(), auto_discardable_);
}
void TabLifecycleUnit::DiscardTab() {
void TabLifecycleUnitSource::TabLifecycleUnit::DiscardTab() {
Discard(DiscardReason::kExternal);
}
bool TabLifecycleUnit::IsDiscarded() const {
bool TabLifecycleUnitSource::TabLifecycleUnit::IsDiscarded() const {
return GetState() == State::DISCARDED;
}
......
......@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
class TabStripModel;
......@@ -22,7 +23,9 @@ namespace resource_coordinator {
class TabLifecycleObserver;
// Represents a tab.
class TabLifecycleUnit : public LifecycleUnit, public TabLifecycleUnitExternal {
class TabLifecycleUnitSource::TabLifecycleUnit
: public LifecycleUnit,
public TabLifecycleUnitExternal {
public:
// |observers| is a list of observers to notify when the discarded state or
// the auto-discardable state of this tab changes. It can be modified outside
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
namespace resource_coordinator {
TabLifecycleUnitSource::TabLifecycleUnitSource()
: browser_tab_strip_tracker_(this, nullptr, this) {
DCHECK(BrowserList::GetInstance()->empty());
browser_tab_strip_tracker_.Init();
}
TabLifecycleUnitSource::~TabLifecycleUnitSource() = default;
void TabLifecycleUnitSource::AddTabLifecycleObserver(
TabLifecycleObserver* observer) {
tab_lifecycle_observers_.AddObserver(observer);
}
void TabLifecycleUnitSource::RemoveTabLifecycleObserver(
TabLifecycleObserver* observer) {
tab_lifecycle_observers_.RemoveObserver(observer);
}
void TabLifecycleUnitSource::SetFocusedTabStripModelForTesting(
TabStripModel* tab_strip) {
focused_tab_strip_model_for_testing_ = tab_strip;
UpdateFocusedTab();
}
TabStripModel* TabLifecycleUnitSource::GetFocusedTabStripModel() const {
if (focused_tab_strip_model_for_testing_)
return focused_tab_strip_model_for_testing_;
Browser* const focused_browser = chrome::FindBrowserWithActiveWindow();
if (!focused_browser)
return nullptr;
return focused_browser->tab_strip_model();
}
void TabLifecycleUnitSource::UpdateFocusedTab() {
TabStripModel* const focused_tab_strip_model = GetFocusedTabStripModel();
content::WebContents* const focused_web_contents =
focused_tab_strip_model ? focused_tab_strip_model->GetActiveWebContents()
: nullptr;
DCHECK(!focused_web_contents ||
base::ContainsKey(tabs_, focused_web_contents));
UpdateFocusedTabTo(focused_web_contents ? tabs_[focused_web_contents].get()
: nullptr);
}
void TabLifecycleUnitSource::UpdateFocusedTabTo(
TabLifecycleUnit* new_focused_tab_lifecycle_unit) {
if (new_focused_tab_lifecycle_unit == focused_tab_lifecycle_unit_)
return;
if (focused_tab_lifecycle_unit_)
focused_tab_lifecycle_unit_->SetFocused(false);
if (new_focused_tab_lifecycle_unit)
new_focused_tab_lifecycle_unit->SetFocused(true);
focused_tab_lifecycle_unit_ = new_focused_tab_lifecycle_unit;
}
void TabLifecycleUnitSource::TabInsertedAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index,
bool foreground) {
auto it = tabs_.find(contents);
if (it == tabs_.end()) {
// A tab was created.
auto res = tabs_.insert(std::make_pair(
contents, std::make_unique<TabLifecycleUnit>(
&tab_lifecycle_observers_, contents, tab_strip_model)));
if (GetFocusedTabStripModel() == tab_strip_model && foreground)
UpdateFocusedTabTo(res.first->second.get());
NotifyLifecycleUnitCreated(res.first->second.get());
} else {
// A tab was moved to another window.
it->second->SetTabStripModel(tab_strip_model);
if (foreground)
UpdateFocusedTab();
}
}
void TabLifecycleUnitSource::TabClosingAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index) {
auto it = tabs_.find(contents);
DCHECK(it != tabs_.end());
TabLifecycleUnit* lifecycle_unit = it->second.get();
if (focused_tab_lifecycle_unit_ == lifecycle_unit)
focused_tab_lifecycle_unit_ = nullptr;
NotifyLifecycleUnitDestroyed(lifecycle_unit);
tabs_.erase(contents);
}
void TabLifecycleUnitSource::ActiveTabChanged(
content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
UpdateFocusedTab();
}
void TabLifecycleUnitSource::TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) {
DCHECK(!base::ContainsKey(tabs_, new_contents));
auto it = tabs_.find(old_contents);
DCHECK(it != tabs_.end());
std::unique_ptr<TabLifecycleUnit> lifecycle_unit = std::move(it->second);
lifecycle_unit->SetWebContents(new_contents);
tabs_.erase(it);
tabs_[new_contents] = std::move(lifecycle_unit);
}
void TabLifecycleUnitSource::OnBrowserSetLastActive(Browser* browser) {
UpdateFocusedTab();
}
void TabLifecycleUnitSource::OnBrowserNoLongerActive(Browser* browser) {
UpdateFocusedTab();
}
} // namespace resource_coordinator
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_SOURCE_H_
#define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_SOURCE_H_
#include <memory>
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_source_base.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/browser_tab_strip_tracker.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
class TabStripModel;
namespace content {
class WebContents;
}
namespace resource_coordinator {
class TabLifecycleObserver;
// Creates and destroys LifecycleUnits as tabs are created and destroyed.
class TabLifecycleUnitSource : public BrowserListObserver,
public LifecycleUnitSourceBase,
public TabStripModelObserver {
public:
TabLifecycleUnitSource();
~TabLifecycleUnitSource() override;
// Adds / removes an observer that is notified when the discarded or auto-
// discardable state of a tab changes.
void AddTabLifecycleObserver(TabLifecycleObserver* observer);
void RemoveTabLifecycleObserver(TabLifecycleObserver* observer);
// Pretend that |tab_strip| is the TabStripModel of the focused window.
void SetFocusedTabStripModelForTesting(TabStripModel* tab_strip);
private:
friend class TabLifecycleUnitTest;
class TabLifecycleUnit;
// Returns the TabStripModel of the focused browser window, if any.
TabStripModel* GetFocusedTabStripModel() const;
// Updates the focused TabLifecycleUnit.
void UpdateFocusedTab();
// Updates the focused TabLifecycleUnit to |new_focused_tab_lifecycle_unit|.
// TabInsertedAt() calls this directly instead of UpdateFocusedTab() because
// the active WebContents of a TabStripModel isn't updated when
// TabInsertedAt() is called.
void UpdateFocusedTabTo(TabLifecycleUnit* new_focused_tab_lifecycle_unit);
// TabStripModelObserver:
void TabInsertedAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index,
bool foreground) override;
void TabClosingAt(TabStripModel* tab_strip_model,
content::WebContents* contents,
int index) override;
void ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) override;
void TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) override;
// BrowserListObserver:
void OnBrowserSetLastActive(Browser* browser) override;
void OnBrowserNoLongerActive(Browser* browser) override;
// Tracks the BrowserList and all TabStripModels.
BrowserTabStripTracker browser_tab_strip_tracker_;
// Pretend that this is the TabStripModel of the focused window, for testing.
TabStripModel* focused_tab_strip_model_for_testing_ = nullptr;
// The currently focused TabLifecycleUnit. Updated by UpdateFocusedTab().
TabLifecycleUnit* focused_tab_lifecycle_unit_ = nullptr;
// Map from content::WebContents to TabLifecycleUnit. Should contain an entry
// for each tab in the current Chrome instance.
base::flat_map<content::WebContents*, std::unique_ptr<TabLifecycleUnit>>
tabs_;
// Observers notified when the discarded or auto-discardable state of a tab
// changes.
base::ObserverList<TabLifecycleObserver> tab_lifecycle_observers_;
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
};
} // namespace resource_coordinator
#endif // CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_SOURCE_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
#include <memory>
#include "base/macros.h"
#include "base/test/simple_test_tick_clock.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/time.h"
#include "chrome/browser/ui/tabs/tab_strip_model_impl.h"
#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.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.h"
#include "content/test/test_web_contents.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace resource_coordinator {
namespace {
constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
class NoUnloadListenerTabStripModelDelegate : public TestTabStripModelDelegate {
public:
NoUnloadListenerTabStripModelDelegate() = default;
bool RunUnloadListenerBeforeClosing(content::WebContents* contents) override {
// The default TestTabStripModelDelegate prevents tabs from being closed.
return false;
}
private:
DISALLOW_COPY_AND_ASSIGN(NoUnloadListenerTabStripModelDelegate);
};
class MockLifecycleUnitSourceObserver : public LifecycleUnitSourceObserver {
public:
MockLifecycleUnitSourceObserver() = default;
MOCK_METHOD1(OnLifecycleUnitCreated, void(LifecycleUnit*));
MOCK_METHOD1(OnLifecycleUnitDestroyed, void(LifecycleUnit*));
private:
DISALLOW_COPY_AND_ASSIGN(MockLifecycleUnitSourceObserver);
};
bool IsFocused(LifecycleUnit* lifecycle_unit) {
return lifecycle_unit->GetSortKey().last_focused_time ==
base::TimeTicks::Max();
}
bool WasNeverFocused(LifecycleUnit* lifecycle_unit) {
return lifecycle_unit->GetSortKey().last_focused_time.is_null();
}
class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
protected:
TabLifecycleUnitSourceTest()
: scoped_set_tick_clock_for_testing_(&test_clock_) {
source_.AddObserver(&observer_);
}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
tab_strip_model_ = std::make_unique<TabStripModelImpl>(
&tab_strip_model_delegate_, profile());
tab_strip_model_->AddObserver(&source_);
}
void TearDown() override {
tab_strip_model_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
// If |focus_tab_strip| is true, focuses the tab strip. Then, appends 2 tabs
// to the tab strip and returns the associated LifecycleUnits via
// |first_lifecycle_unit| and |second_lifecycle_unit|. The first tab is
// background and the second tab is active.
void CreateTwoTabs(bool focus_tab_strip,
LifecycleUnit** first_lifecycle_unit,
LifecycleUnit** second_lifecycle_unit) {
if (focus_tab_strip)
source_.SetFocusedTabStripModelForTesting(tab_strip_model_.get());
// Add a foreground tab to the tab strip.
test_clock_.Advance(kShortDelay);
EXPECT_CALL(observer_, OnLifecycleUnitCreated(testing::_))
.WillOnce(testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
*first_lifecycle_unit = lifecycle_unit;
if (focus_tab_strip)
EXPECT_TRUE(IsFocused(*first_lifecycle_unit));
else
EXPECT_TRUE(WasNeverFocused(*first_lifecycle_unit));
}));
content::WebContents* first_web_contents = CreateTestWebContents();
tab_strip_model_->AppendWebContents(first_web_contents, true);
testing::Mock::VerifyAndClear(&observer_);
// Add another foreground tab to the focused tab strip.
test_clock_.Advance(kShortDelay);
auto time_before_second_tab = test_clock_.NowTicks();
EXPECT_CALL(observer_, OnLifecycleUnitCreated(testing::_))
.WillOnce(testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
*second_lifecycle_unit = lifecycle_unit;
if (focus_tab_strip) {
EXPECT_EQ(time_before_second_tab,
(*first_lifecycle_unit)->GetSortKey().last_focused_time);
EXPECT_TRUE(IsFocused(*second_lifecycle_unit));
} else {
EXPECT_TRUE(WasNeverFocused(*first_lifecycle_unit));
EXPECT_TRUE(WasNeverFocused(*second_lifecycle_unit));
}
}));
content::WebContents* second_web_contents = CreateTestWebContents();
tab_strip_model_->AppendWebContents(second_web_contents, true);
testing::Mock::VerifyAndClear(&observer_);
}
void TestAppendTabsToTabStrip(bool focus_tab_strip) {
LifecycleUnit* first_lifecycle_unit = nullptr;
LifecycleUnit* second_lifecycle_unit = nullptr;
CreateTwoTabs(focus_tab_strip, &first_lifecycle_unit,
&second_lifecycle_unit);
const base::TimeTicks first_tab_last_focused_time =
first_lifecycle_unit->GetSortKey().last_focused_time;
// Add a background tab to the focused tab strip.
test_clock_.Advance(kShortDelay);
LifecycleUnit* third_lifecycle_unit = nullptr;
EXPECT_CALL(observer_, OnLifecycleUnitCreated(testing::_))
.WillOnce(testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
third_lifecycle_unit = lifecycle_unit;
if (focus_tab_strip) {
EXPECT_EQ(first_tab_last_focused_time,
first_lifecycle_unit->GetSortKey().last_focused_time);
EXPECT_TRUE(IsFocused(second_lifecycle_unit));
} else {
EXPECT_TRUE(WasNeverFocused(first_lifecycle_unit));
EXPECT_TRUE(WasNeverFocused(second_lifecycle_unit));
}
EXPECT_TRUE(WasNeverFocused(third_lifecycle_unit));
}));
content::WebContents* third_web_contents = CreateTestWebContents();
tab_strip_model_->AppendWebContents(third_web_contents, false);
testing::Mock::VerifyAndClear(&observer_);
// Expect notifications when tabs are closed.
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(first_lifecycle_unit));
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(second_lifecycle_unit));
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(third_lifecycle_unit));
tab_strip_model_->CloseAllTabs();
}
TabLifecycleUnitSource source_;
testing::StrictMock<MockLifecycleUnitSourceObserver> observer_;
std::unique_ptr<TabStripModel> tab_strip_model_;
base::SimpleTestTickClock test_clock_;
private:
NoUnloadListenerTabStripModelDelegate tab_strip_model_delegate_;
ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSourceTest);
};
} // namespace
TEST_F(TabLifecycleUnitSourceTest, AppendTabsToFocusedTabStrip) {
TestAppendTabsToTabStrip(true /* focus_tab_strip */);
}
TEST_F(TabLifecycleUnitSourceTest, AppendTabsToNonFocusedTabStrip) {
TestAppendTabsToTabStrip(false /* focus_tab_strip */);
}
TEST_F(TabLifecycleUnitSourceTest, SwitchTabInFocusedTabStrip) {
LifecycleUnit* first_lifecycle_unit = nullptr;
LifecycleUnit* second_lifecycle_unit = nullptr;
CreateTwoTabs(true /* focus_tab_strip */, &first_lifecycle_unit,
&second_lifecycle_unit);
// Activate the first tab.
test_clock_.Advance(kShortDelay);
auto time_before_activate = test_clock_.NowTicks();
tab_strip_model_->ActivateTabAt(0, true);
EXPECT_TRUE(IsFocused(first_lifecycle_unit));
EXPECT_EQ(time_before_activate,
second_lifecycle_unit->GetSortKey().last_focused_time);
// Expect notifications when tabs are closed.
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(first_lifecycle_unit));
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(second_lifecycle_unit));
tab_strip_model_->CloseAllTabs();
}
TEST_F(TabLifecycleUnitSourceTest, CloseTabInFocusedTabStrip) {
LifecycleUnit* first_lifecycle_unit = nullptr;
LifecycleUnit* second_lifecycle_unit = nullptr;
CreateTwoTabs(true /* focus_tab_strip */, &first_lifecycle_unit,
&second_lifecycle_unit);
// Close the second tab. The first tab should be focused.
test_clock_.Advance(kShortDelay);
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(second_lifecycle_unit));
tab_strip_model_->CloseWebContentsAt(1, 0);
testing::Mock::VerifyAndClear(&observer_);
EXPECT_TRUE(IsFocused(first_lifecycle_unit));
// Expect notifications when tabs are closed.
EXPECT_CALL(observer_, OnLifecycleUnitDestroyed(first_lifecycle_unit));
tab_strip_model_->CloseAllTabs();
}
} // namespace resource_coordinator
......@@ -11,6 +11,7 @@
#include "base/test/simple_test_tick_clock.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
#include "chrome/browser/resource_coordinator/time.h"
#include "chrome/browser/ui/tabs/tab_strip_model_impl.h"
#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
......@@ -43,6 +44,8 @@ class MockTabLifecycleObserver : public TabLifecycleObserver {
class TabLifecycleUnitTest : public ChromeRenderViewHostTestHarness {
protected:
using TabLifecycleUnit = TabLifecycleUnitSource::TabLifecycleUnit;
TabLifecycleUnitTest() : scoped_set_tick_clock_for_testing_(&test_clock_) {
observers_.AddObserver(&observer_);
}
......
......@@ -2767,6 +2767,7 @@ test("unit_tests") {
"../browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc",
"../browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc",
"../browser/resource_coordinator/lifecycle_unit_unittest.cc",
"../browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc",
"../browser/resource_coordinator/tab_lifecycle_unit_unittest.cc",
"../browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc",
"../browser/resource_coordinator/tab_manager_stats_collector_unittest.cc",
......
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