Commit 86653719 authored by andersoncss's avatar andersoncss Committed by Commit bot

Discardable property support on TabManager

BUG=621070

Review-Url: https://codereview.chromium.org/2167843004
Cr-Commit-Position: refs/heads/master@{#407258}
parent 55c66f40
...@@ -617,6 +617,10 @@ bool TabManager::CanDiscardTab(int64_t target_web_contents_id) const { ...@@ -617,6 +617,10 @@ bool TabManager::CanDiscardTab(int64_t target_web_contents_id) const {
return false; return false;
} }
// Do not discard a tab that was explicitly disallowed to.
if (!IsTabAutoDiscardable(web_contents))
return false;
return true; return true;
} }
...@@ -728,6 +732,10 @@ bool TabManager::CompareTabStats(TabStats first, TabStats second) { ...@@ -728,6 +732,10 @@ bool TabManager::CompareTabStats(TabStats first, TabStats second) {
if (first.is_selected != second.is_selected) if (first.is_selected != second.is_selected)
return first.is_selected; return first.is_selected;
// Non auto-discardable tabs are more important to protect.
if (first.is_auto_discardable != second.is_auto_discardable)
return !first.is_auto_discardable;
// Protect tabs with pending form entries. // Protect tabs with pending form entries.
if (first.has_form_entry != second.has_form_entry) if (first.has_form_entry != second.has_form_entry)
return first.has_form_entry; return first.has_form_entry;
...@@ -906,4 +914,20 @@ void TabManager::OnDiscardedStateChange(content::WebContents* contents, ...@@ -906,4 +914,20 @@ void TabManager::OnDiscardedStateChange(content::WebContents* contents,
OnDiscardedStateChange(contents, is_discarded)); OnDiscardedStateChange(contents, is_discarded));
} }
void TabManager::OnAutoDiscardableStateChange(content::WebContents* contents,
bool is_auto_discardable) {
FOR_EACH_OBSERVER(
TabManagerObserver, observers_,
OnAutoDiscardableStateChange(contents, is_auto_discardable));
}
bool TabManager::IsTabAutoDiscardable(content::WebContents* contents) const {
return GetWebContentsData(contents)->IsAutoDiscardable();
}
void TabManager::SetTabAutoDiscardableState(content::WebContents* contents,
bool state) {
GetWebContentsData(contents)->SetAutoDiscardableState(state);
}
} // namespace memory } // namespace memory
...@@ -128,7 +128,18 @@ class TabManager : public TabStripModelObserver { ...@@ -128,7 +128,18 @@ class TabManager : public TabStripModelObserver {
void AddObserver(TabManagerObserver* observer); void AddObserver(TabManagerObserver* observer);
void RemoveObserver(TabManagerObserver* observer); void RemoveObserver(TabManagerObserver* observer);
// Returns the auto-discardable state of the tab. When true, the tab is
// eligible to be automatically discarded when critical memory pressure hits,
// otherwise the tab is ignored and will never be automatically discarded.
// Note that this property doesn't block the discarding of the tab via other
// methods (about:discards for instance).
bool IsTabAutoDiscardable(content::WebContents* contents) const;
// Sets/clears the auto-discardable state of the tab.
void SetTabAutoDiscardableState(content::WebContents* contents, bool state);
private: private:
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, Comparator); FRIEND_TEST_ALL_PREFIXES(TabManagerTest, Comparator);
...@@ -151,6 +162,11 @@ class TabManager : public TabStripModelObserver { ...@@ -151,6 +162,11 @@ class TabManager : public TabStripModelObserver {
void OnDiscardedStateChange(content::WebContents* contents, void OnDiscardedStateChange(content::WebContents* contents,
bool is_discarded); bool is_discarded);
// Called by WebContentsData whenever the auto-discardable state of a
// WebContents changes, so that observers can be informed.
void OnAutoDiscardableStateChange(content::WebContents* contents,
bool is_auto_discardable);
// The time that a renderer is given to react to a memory pressure // The time that a renderer is given to react to a memory pressure
// notification before another renderer is also notified. This prevents all // notification before another renderer is also notified. This prevents all
// renderers from receiving and acting upon notifications simultaneously, // renderers from receiving and acting upon notifications simultaneously,
......
...@@ -393,6 +393,46 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectVideoTabs) { ...@@ -393,6 +393,46 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectVideoTabs) {
EXPECT_TRUE(tab_manager->DiscardTabImpl()); EXPECT_TRUE(tab_manager->DiscardTabImpl());
} }
IN_PROC_BROWSER_TEST_F(TabManagerTest, AutoDiscardable) {
using content::WindowedNotificationObserver;
TabManager* tab_manager = g_browser_process->GetTabManager();
// Disable the protection of recent tabs.
tab_manager->minimum_protection_time_ = base::TimeDelta::FromMinutes(0);
// Get two tabs open.
WindowedNotificationObserver load1(
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::NotificationService::AllSources());
OpenURLParams open1(GURL(chrome::kChromeUIAboutURL), content::Referrer(),
CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
browser()->OpenURL(open1);
load1.Wait();
WindowedNotificationObserver load2(
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::NotificationService::AllSources());
OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(),
NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
browser()->OpenURL(open2);
load2.Wait();
// Set the auto-discardable state of the first tab to false.
auto tsm = browser()->tab_strip_model();
ASSERT_EQ(2, tsm->count());
tab_manager->SetTabAutoDiscardableState(tsm->GetWebContentsAt(0), false);
// Shouldn't discard the tab, since auto-discardable is deactivated.
EXPECT_FALSE(tab_manager->DiscardTabImpl());
// Reset auto-discardable state to true.
tab_manager->SetTabAutoDiscardableState(tsm->GetWebContentsAt(0), true);
// Now it should be able to discard the tab.
EXPECT_TRUE(tab_manager->DiscardTabImpl());
EXPECT_TRUE(tab_manager->IsTabDiscarded(tsm->GetWebContentsAt(0)));
}
} // namespace memory } // namespace memory
#endif // OS_WIN || OS_MAXOSX || OS_LINUX #endif // OS_WIN || OS_MAXOSX || OS_LINUX
...@@ -13,6 +13,10 @@ namespace memory { ...@@ -13,6 +13,10 @@ namespace memory {
void TabManagerObserver::OnDiscardedStateChange(content::WebContents* contents, void TabManagerObserver::OnDiscardedStateChange(content::WebContents* contents,
bool is_discarded) {} bool is_discarded) {}
void TabManagerObserver::OnAutoDiscardableStateChange(
content::WebContents* contents,
bool is_auto_discardable) {}
TabManagerObserver::~TabManagerObserver() {} TabManagerObserver::~TabManagerObserver() {}
} // namespace memory } // namespace memory
...@@ -20,6 +20,10 @@ class TabManagerObserver { ...@@ -20,6 +20,10 @@ class TabManagerObserver {
virtual void OnDiscardedStateChange(content::WebContents* contents, virtual void OnDiscardedStateChange(content::WebContents* contents,
bool is_discarded); bool is_discarded);
// Invoked when the auto-discardable state changes.
virtual void OnAutoDiscardableStateChange(content::WebContents* contents,
bool is_auto_discardable);
protected: protected:
virtual ~TabManagerObserver(); virtual ~TabManagerObserver();
}; };
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/memory/tab_manager.h" #include "chrome/browser/memory/tab_manager.h"
#include "chrome/browser/memory/tab_manager_observer.h" #include "chrome/browser/memory/tab_manager_observer.h"
#include "chrome/browser/memory/tab_manager_web_contents_data.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
...@@ -46,7 +47,10 @@ class TabManagerObserverTest : public InProcessBrowserTest { ...@@ -46,7 +47,10 @@ class TabManagerObserverTest : public InProcessBrowserTest {
class MockTabManagerObserver : public TabManagerObserver { class MockTabManagerObserver : public TabManagerObserver {
public: public:
MockTabManagerObserver() MockTabManagerObserver()
: nb_events_(0), contents_(nullptr), is_discarded_(false) {} : nb_events_(0),
contents_(nullptr),
is_discarded_(false),
is_auto_discardable_(true) {}
// TabManagerObserver implementation: // TabManagerObserver implementation:
void OnDiscardedStateChange(content::WebContents* contents, void OnDiscardedStateChange(content::WebContents* contents,
...@@ -56,14 +60,23 @@ class MockTabManagerObserver : public TabManagerObserver { ...@@ -56,14 +60,23 @@ class MockTabManagerObserver : public TabManagerObserver {
is_discarded_ = is_discarded; is_discarded_ = is_discarded;
} }
void OnAutoDiscardableStateChange(content::WebContents* contents,
bool is_auto_discardable) override {
nb_events_++;
contents_ = contents;
is_auto_discardable_ = is_auto_discardable;
}
int nb_events() const { return nb_events_; } int nb_events() const { return nb_events_; }
WebContents* content() const { return contents_; } WebContents* content() const { return contents_; }
bool is_discarded() const { return is_discarded_; } bool is_discarded() const { return is_discarded_; }
bool is_auto_discardable() const { return is_auto_discardable_; }
private: private:
int nb_events_; int nb_events_;
WebContents* contents_; WebContents* contents_;
bool is_discarded_; bool is_discarded_;
bool is_auto_discardable_;
DISALLOW_COPY_AND_ASSIGN(MockTabManagerObserver); DISALLOW_COPY_AND_ASSIGN(MockTabManagerObserver);
}; };
...@@ -131,6 +144,45 @@ IN_PROC_BROWSER_TEST_F(TabManagerObserverTest, OnDiscardStateChange) { ...@@ -131,6 +144,45 @@ IN_PROC_BROWSER_TEST_F(TabManagerObserverTest, OnDiscardStateChange) {
EXPECT_EQ(4, tabmanager_observer.nb_events()); EXPECT_EQ(4, tabmanager_observer.nb_events());
} }
IN_PROC_BROWSER_TEST_F(TabManagerObserverTest, OnAutoDiscardableStateChange) {
TabManager* tab_manager = g_browser_process->GetTabManager();
ASSERT_TRUE(tab_manager);
auto tsm = browser()->tab_strip_model();
set_tab_strip_model(tsm);
// Open two tabs.
OpenURLParams open(GURL(chrome::kChromeUIAboutURL), content::Referrer(),
NEW_BACKGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
WebContents* contents = browser()->OpenURL(open);
// Subscribe observer to TabManager's observer list.
MockTabManagerObserver observer;
tab_manager->AddObserver(&observer);
// No events initially.
EXPECT_EQ(0, observer.nb_events());
// Should maintain at zero since the default value of the state is true.
tab_manager->SetTabAutoDiscardableState(contents, true);
EXPECT_EQ(0, observer.nb_events());
// Now it has to change.
tab_manager->SetTabAutoDiscardableState(contents, false);
EXPECT_EQ(1, observer.nb_events());
EXPECT_FALSE(observer.is_auto_discardable());
EXPECT_EQ(ContentsId(contents), ContentsId(observer.content()));
// No changes since it's not a new state.
tab_manager->SetTabAutoDiscardableState(contents, false);
EXPECT_EQ(1, observer.nb_events());
// Change it back and we should have another event.
tab_manager->SetTabAutoDiscardableState(contents, true);
EXPECT_EQ(2, observer.nb_events());
EXPECT_TRUE(observer.is_auto_discardable());
EXPECT_EQ(ContentsId(contents), ContentsId(observer.content()));
}
} // namespace memory } // namespace memory
#endif // OS_WIN || OS_MAXOSX || OS_LINUX #endif // OS_WIN || OS_MAXOSX || OS_LINUX
...@@ -174,6 +174,7 @@ class TaskRunnerProxy : public base::TaskRunner { ...@@ -174,6 +174,7 @@ class TaskRunnerProxy : public base::TaskRunner {
enum TestIndicies { enum TestIndicies {
kSelected, kSelected,
kAutoDiscardable,
kPinned, kPinned,
kApp, kApp,
kPlayingAudio, kPlayingAudio,
...@@ -277,6 +278,14 @@ TEST_F(TabManagerTest, Comparator) { ...@@ -277,6 +278,14 @@ TEST_F(TabManagerTest, Comparator) {
test_list.push_back(stats); test_list.push_back(stats);
} }
{
TabStats stats;
stats.last_active = now;
stats.is_auto_discardable = false;
stats.child_process_host_id = kAutoDiscardable;
test_list.push_back(stats);
}
// This entry sorts to the front, so by adding it last, it verifies that the // This entry sorts to the front, so by adding it last, it verifies that the
// array is being sorted. // array is being sorted.
{ {
...@@ -291,6 +300,7 @@ TEST_F(TabManagerTest, Comparator) { ...@@ -291,6 +300,7 @@ TEST_F(TabManagerTest, Comparator) {
int index = 0; int index = 0;
EXPECT_EQ(kSelected, test_list[index++].child_process_host_id); EXPECT_EQ(kSelected, test_list[index++].child_process_host_id);
EXPECT_EQ(kAutoDiscardable, test_list[index++].child_process_host_id);
EXPECT_EQ(kFormEntry, test_list[index++].child_process_host_id); EXPECT_EQ(kFormEntry, test_list[index++].child_process_host_id);
EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id); EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id);
EXPECT_EQ(kPinned, test_list[index++].child_process_host_id); EXPECT_EQ(kPinned, test_list[index++].child_process_host_id);
......
...@@ -98,12 +98,8 @@ void TabManager::WebContentsData::SetDiscardState(bool state) { ...@@ -98,12 +98,8 @@ void TabManager::WebContentsData::SetDiscardState(bool state) {
} }
tab_data_.is_discarded_ = state; tab_data_.is_discarded_ = state;
// TabManager could not exist in tests.
if (g_browser_process->GetTabManager()) {
g_browser_process->GetTabManager()->OnDiscardedStateChange(web_contents(), g_browser_process->GetTabManager()->OnDiscardedStateChange(web_contents(),
state); state);
}
} }
int TabManager::WebContentsData::DiscardCount() { int TabManager::WebContentsData::DiscardCount() {
...@@ -172,7 +168,8 @@ TabManager::WebContentsData::Data::Data() ...@@ -172,7 +168,8 @@ TabManager::WebContentsData::Data::Data()
last_discard_time_(TimeTicks::UnixEpoch()), last_discard_time_(TimeTicks::UnixEpoch()),
last_reload_time_(TimeTicks::UnixEpoch()), last_reload_time_(TimeTicks::UnixEpoch()),
last_inactive_time_(TimeTicks::UnixEpoch()), last_inactive_time_(TimeTicks::UnixEpoch()),
engagement_score_(-1.0) {} engagement_score_(-1.0),
is_auto_discardable(true) {}
bool TabManager::WebContentsData::Data::operator==(const Data& right) const { bool TabManager::WebContentsData::Data::operator==(const Data& right) const {
return is_discarded_ == right.is_discarded_ && return is_discarded_ == right.is_discarded_ &&
...@@ -188,4 +185,17 @@ bool TabManager::WebContentsData::Data::operator!=(const Data& right) const { ...@@ -188,4 +185,17 @@ bool TabManager::WebContentsData::Data::operator!=(const Data& right) const {
return !(*this == right); return !(*this == right);
} }
void TabManager::WebContentsData::SetAutoDiscardableState(bool state) {
if (tab_data_.is_auto_discardable == state)
return;
tab_data_.is_auto_discardable = state;
g_browser_process->GetTabManager()->OnAutoDiscardableStateChange(
web_contents(), state);
}
bool TabManager::WebContentsData::IsAutoDiscardable() {
return tab_data_.is_auto_discardable;
}
} // namespace memory } // namespace memory
...@@ -72,6 +72,13 @@ class TabManager::WebContentsData ...@@ -72,6 +72,13 @@ class TabManager::WebContentsData
// |test_tick_clock_| for more details. // |test_tick_clock_| for more details.
void set_test_tick_clock(base::TickClock* test_tick_clock); void set_test_tick_clock(base::TickClock* test_tick_clock);
// Returns the auto-discardable state of the tab.
// See tab_manager.h for more information.
bool IsAutoDiscardable();
// Sets/clears the auto-discardable state of the tab.
void SetAutoDiscardableState(bool state);
private: private:
// Needed to access tab_data_. // Needed to access tab_data_.
FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, CopyState); FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, CopyState);
...@@ -99,6 +106,8 @@ class TabManager::WebContentsData ...@@ -99,6 +106,8 @@ class TabManager::WebContentsData
base::TimeTicks last_inactive_time_; base::TimeTicks last_inactive_time_;
// Site Engagement score (set to -1 if not available). // Site Engagement score (set to -1 if not available).
double engagement_score_; double engagement_score_;
// Is tab eligible for auto discarding? Defaults to true.
bool is_auto_discardable;
}; };
// Returns either the system's clock or the test clock. See |test_tick_clock_| // Returns either the system's clock or the test clock. See |test_tick_clock_|
......
...@@ -22,7 +22,8 @@ TabStats::TabStats() ...@@ -22,7 +22,8 @@ TabStats::TabStats()
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
oom_score(0), oom_score(0),
#endif #endif
tab_contents_id(0) { tab_contents_id(0),
is_auto_discardable(true) {
} }
TabStats::TabStats(const TabStats& other) = default; TabStats::TabStats(const TabStats& other) = default;
......
...@@ -41,6 +41,7 @@ struct TabStats { ...@@ -41,6 +41,7 @@ struct TabStats {
int oom_score; int oom_score;
#endif #endif
int64_t tab_contents_id; // Unique ID per WebContents. int64_t tab_contents_id; // Unique ID per WebContents.
bool is_auto_discardable;
}; };
typedef std::vector<TabStats> TabStatsList; typedef std::vector<TabStats> TabStatsList;
......
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