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 {
return false;
}
// Do not discard a tab that was explicitly disallowed to.
if (!IsTabAutoDiscardable(web_contents))
return false;
return true;
}
......@@ -728,6 +732,10 @@ bool TabManager::CompareTabStats(TabStats first, TabStats second) {
if (first.is_selected != second.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.
if (first.has_form_entry != second.has_form_entry)
return first.has_form_entry;
......@@ -906,4 +914,20 @@ void TabManager::OnDiscardedStateChange(content::WebContents* contents,
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
......@@ -128,7 +128,18 @@ class TabManager : public TabStripModelObserver {
void AddObserver(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:
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ChildProcessNotifications);
FRIEND_TEST_ALL_PREFIXES(TabManagerTest, Comparator);
......@@ -151,6 +162,11 @@ class TabManager : public TabStripModelObserver {
void OnDiscardedStateChange(content::WebContents* contents,
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
// notification before another renderer is also notified. This prevents all
// renderers from receiving and acting upon notifications simultaneously,
......
......@@ -393,6 +393,46 @@ IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectVideoTabs) {
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
#endif // OS_WIN || OS_MAXOSX || OS_LINUX
......@@ -13,6 +13,10 @@ namespace memory {
void TabManagerObserver::OnDiscardedStateChange(content::WebContents* contents,
bool is_discarded) {}
void TabManagerObserver::OnAutoDiscardableStateChange(
content::WebContents* contents,
bool is_auto_discardable) {}
TabManagerObserver::~TabManagerObserver() {}
} // namespace memory
......@@ -20,6 +20,10 @@ class TabManagerObserver {
virtual void OnDiscardedStateChange(content::WebContents* contents,
bool is_discarded);
// Invoked when the auto-discardable state changes.
virtual void OnAutoDiscardableStateChange(content::WebContents* contents,
bool is_auto_discardable);
protected:
virtual ~TabManagerObserver();
};
......
......@@ -6,6 +6,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/memory/tab_manager.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/tabs/tab_strip_model.h"
#include "chrome/common/url_constants.h"
......@@ -46,7 +47,10 @@ class TabManagerObserverTest : public InProcessBrowserTest {
class MockTabManagerObserver : public TabManagerObserver {
public:
MockTabManagerObserver()
: nb_events_(0), contents_(nullptr), is_discarded_(false) {}
: nb_events_(0),
contents_(nullptr),
is_discarded_(false),
is_auto_discardable_(true) {}
// TabManagerObserver implementation:
void OnDiscardedStateChange(content::WebContents* contents,
......@@ -56,14 +60,23 @@ class MockTabManagerObserver : public TabManagerObserver {
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_; }
WebContents* content() const { return contents_; }
bool is_discarded() const { return is_discarded_; }
bool is_auto_discardable() const { return is_auto_discardable_; }
private:
int nb_events_;
WebContents* contents_;
bool is_discarded_;
bool is_auto_discardable_;
DISALLOW_COPY_AND_ASSIGN(MockTabManagerObserver);
};
......@@ -131,6 +144,45 @@ IN_PROC_BROWSER_TEST_F(TabManagerObserverTest, OnDiscardStateChange) {
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
#endif // OS_WIN || OS_MAXOSX || OS_LINUX
......@@ -174,6 +174,7 @@ class TaskRunnerProxy : public base::TaskRunner {
enum TestIndicies {
kSelected,
kAutoDiscardable,
kPinned,
kApp,
kPlayingAudio,
......@@ -277,6 +278,14 @@ TEST_F(TabManagerTest, Comparator) {
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
// array is being sorted.
{
......@@ -291,6 +300,7 @@ TEST_F(TabManagerTest, Comparator) {
int index = 0;
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(kPlayingAudio, 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) {
}
tab_data_.is_discarded_ = state;
// TabManager could not exist in tests.
if (g_browser_process->GetTabManager()) {
g_browser_process->GetTabManager()->OnDiscardedStateChange(web_contents(),
state);
}
g_browser_process->GetTabManager()->OnDiscardedStateChange(web_contents(),
state);
}
int TabManager::WebContentsData::DiscardCount() {
......@@ -172,7 +168,8 @@ TabManager::WebContentsData::Data::Data()
last_discard_time_(TimeTicks::UnixEpoch()),
last_reload_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 {
return is_discarded_ == right.is_discarded_ &&
......@@ -188,4 +185,17 @@ bool TabManager::WebContentsData::Data::operator!=(const Data& right) const {
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
......@@ -72,6 +72,13 @@ class TabManager::WebContentsData
// |test_tick_clock_| for more details.
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:
// Needed to access tab_data_.
FRIEND_TEST_ALL_PREFIXES(TabManagerWebContentsDataTest, CopyState);
......@@ -99,6 +106,8 @@ class TabManager::WebContentsData
base::TimeTicks last_inactive_time_;
// Site Engagement score (set to -1 if not available).
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_|
......
......@@ -22,7 +22,8 @@ TabStats::TabStats()
#if defined(OS_CHROMEOS)
oom_score(0),
#endif
tab_contents_id(0) {
tab_contents_id(0),
is_auto_discardable(true) {
}
TabStats::TabStats(const TabStats& other) = default;
......
......@@ -41,6 +41,7 @@ struct TabStats {
int oom_score;
#endif
int64_t tab_contents_id; // Unique ID per WebContents.
bool is_auto_discardable;
};
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