summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgeorgesak <georgesak@chromium.org>2015-05-22 15:08:07 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-22 22:09:02 +0000
commit5582cbe405f6dc48b14e2cae7254aa03abcf353b (patch)
tree8d47eeb46e67fca1cd2dd73aad73fb5ab5a5a256
parentf564cedfa2331f9fd4a0e4a1c511ca8459ae243a (diff)
downloadchromium_src-5582cbe405f6dc48b14e2cae7254aa03abcf353b.zip
chromium_src-5582cbe405f6dc48b14e2cae7254aa03abcf353b.tar.gz
chromium_src-5582cbe405f6dc48b14e2cae7254aa03abcf353b.tar.bz2
[Session restore] Add MRU logic to loading of background pages.
This patch adds the notion of last activation time to tabs and when a session restores, background tabs are loaded using MRU. BUG=472772 Review URL: https://codereview.chromium.org/1131373003 Cr-Commit-Position: refs/heads/master@{#331189}
-rw-r--r--chrome/browser/sessions/session_restore.cc37
-rw-r--r--chrome/browser/sessions/session_restore_browsertest.cc139
-rw-r--r--chrome/browser/sessions/session_restore_delegate.cc6
-rw-r--r--chrome/browser/sessions/session_service.cc17
-rw-r--r--chrome/browser/sessions/session_service.h5
-rw-r--r--chrome/browser/ui/browser.cc14
-rw-r--r--components/sessions/session_service_commands.cc30
-rw-r--r--components/sessions/session_service_commands.h3
-rw-r--r--components/sessions/session_types.h5
-rw-r--r--content/browser/web_contents/web_contents_impl.cc4
-rw-r--r--content/browser/web_contents/web_contents_impl.h1
-rw-r--r--content/public/browser/web_contents.h5
12 files changed, 245 insertions, 21 deletions
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index c669216..b1a7498 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -297,10 +297,11 @@ class SessionRestoreImpl : public content::NotificationObserver {
}
if (succeeded) {
- // Start Loading tabs.
if (SessionRestore::GetSmartRestoreMode() !=
- SessionRestore::SMART_RESTORE_MODE_OFF)
+ SessionRestore::SMART_RESTORE_MODE_OFF) {
std::stable_sort(contents_created->begin(), contents_created->end());
+ }
+ // Start Loading tabs.
SessionRestoreDelegate::RestoreTabs(*contents_created, restore_started_);
}
@@ -507,7 +508,26 @@ class SessionRestoreImpl : public content::NotificationObserver {
std::vector<RestoredTab>* created_contents) {
DVLOG(1) << "RestoreTabsToBrowser " << window.tabs.size();
DCHECK(!window.tabs.empty());
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeTicks highest_time = base::TimeTicks::UnixEpoch();
if (initial_tab_count == 0) {
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU) {
+ // The last active time of a WebContents is initially set to the
+ // creation time of the tab, which is not necessarly the same as the
+ // loading time, so we have to restore the values. Also, since TimeTicks
+ // only make sense in their current session, these values have to be
+ // sanitized first. To do so, we need to first figure out the largest
+ // time. This will then be used to set the last active time of
+ // each tab where the most recent tab will have its time set to |now|
+ // and the rest of the tabs will have theirs set earlier by the same
+ // delta as they originally had.
+ for (int i = 0; i < static_cast<int>(window.tabs.size()); ++i) {
+ const sessions::SessionTab& tab = *(window.tabs[i]);
+ if (tab.last_active_time > highest_time)
+ highest_time = tab.last_active_time;
+ }
+ }
for (int i = 0; i < static_cast<int>(window.tabs.size()); ++i) {
const sessions::SessionTab& tab = *(window.tabs[i]);
@@ -520,6 +540,13 @@ class SessionRestoreImpl : public content::NotificationObserver {
if (!contents)
continue;
+ // Sanitize the last active time.
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU) {
+ base::TimeDelta delta = highest_time - tab.last_active_time;
+ contents->SetLastActiveTime(now - delta);
+ }
+
RestoredTab restored_tab(contents, is_selected_tab,
tab.extension_app_id.empty(), tab.pinned);
created_contents->push_back(restored_tab);
@@ -544,6 +571,12 @@ class SessionRestoreImpl : public content::NotificationObserver {
WebContents* contents =
RestoreTab(tab, tab_index_offset + i, browser, false);
if (contents) {
+ // Sanitize the last active time.
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU) {
+ base::TimeDelta delta = highest_time - tab.last_active_time;
+ contents->SetLastActiveTime(now - delta);
+ }
RestoredTab restored_tab(contents, false,
tab.extension_app_id.empty(), tab.pinned);
created_contents->push_back(restored_tab);
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 14966d7..647e499 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -212,12 +212,16 @@ class SessionRestoreTest : public InProcessBrowserTest {
const BrowserList* active_browser_list_;
};
-// Activates the smart restore behaviour and can track the loading of tabs.
-class SmartSessionRestoreTest : public SessionRestoreTest,
- public content::NotificationObserver {
+// Activates the smart restore behaviour in "simple" mode and tracks the loading
+// of tabs.
+class SmartSessionRestoreSimpleTest : public SessionRestoreTest,
+ public content::NotificationObserver {
public:
- SmartSessionRestoreTest() {}
+ SmartSessionRestoreSimpleTest() {}
void StartObserving(int num_tabs) {
+ // Start by clearing everything so it can be reused in the same test.
+ web_contents_.clear();
+ registrar_.RemoveAll();
num_tabs_ = num_tabs;
registrar_.Add(this, content::NOTIFICATION_LOAD_START,
content::NotificationService::AllSources());
@@ -261,7 +265,24 @@ class SmartSessionRestoreTest : public SessionRestoreTest,
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
int num_tabs_;
- DISALLOW_COPY_AND_ASSIGN(SmartSessionRestoreTest);
+ DISALLOW_COPY_AND_ASSIGN(SmartSessionRestoreSimpleTest);
+};
+
+class SmartSessionRestoreMRUTest : public SmartSessionRestoreSimpleTest {
+ public:
+ SmartSessionRestoreMRUTest() {}
+
+ protected:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kForceFieldTrials, "IntelligentSessionRestore/TestGroup/");
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kForceFieldTrialParams,
+ "IntelligentSessionRestore.TestGroup:PrioritizeTabs/mru");
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SmartSessionRestoreMRUTest);
};
// Verifies that restored tabs have a root window. This is important
@@ -1333,14 +1354,14 @@ IN_PROC_BROWSER_TEST_F(SessionRestoreTest, SessionStorageAfterTabReplace) {
EXPECT_EQ(1, new_browser->tab_strip_model()->count());
}
-IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
+IN_PROC_BROWSER_TEST_F(SmartSessionRestoreSimpleTest, CorrectLoadingOrder) {
ASSERT_EQ(SessionRestore::SMART_RESTORE_MODE_SIMPLE,
SessionRestore::GetSmartRestoreMode());
- const int NumTabs = 6;
+ const int num_tabs = 6;
// Start observing the loading of tabs, to make sure the order is correct.
- StartObserving(NumTabs);
+ StartObserving(num_tabs);
struct TabInfo {
GURL url;
@@ -1348,7 +1369,7 @@ IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
int expected_load_order;
};
- TabInfo tab_info[NumTabs] = {
+ TabInfo tab_info[num_tabs] = {
// This will be the foreground tab and will always load first.
{GURL("http://google.com/1"), false, 1},
{GURL("http://google.com/2"), false, 3},
@@ -1362,9 +1383,9 @@ IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
// Set up the restore data.
std::vector<const sessions::SessionWindow*> session;
sessions::SessionWindow window;
- sessions::SessionTab tab[NumTabs];
+ sessions::SessionTab tab[num_tabs];
- for (int i = 0; i < NumTabs; i++) {
+ for (int i = 0; i < num_tabs; i++) {
SerializedNavigationEntry nav =
SerializedNavigationEntryTestHelper::CreateNavigation(
tab_info[i].url.spec(), tab_info[i].url.spec().c_str());
@@ -1379,17 +1400,16 @@ IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
session.push_back(&window);
Profile* profile = browser()->profile();
- ui_test_utils::BrowserAddedObserver window_observer;
std::vector<Browser*> browsers = SessionRestore::RestoreForeignSessionWindows(
profile, browser()->host_desktop_type(), session.begin(), session.end());
ASSERT_EQ(1u, browsers.size());
ASSERT_TRUE(browsers[0]);
- ASSERT_EQ(NumTabs, browsers[0]->tab_strip_model()->count());
+ ASSERT_EQ(num_tabs, browsers[0]->tab_strip_model()->count());
WaitForAllTabsToStartLoading();
- ASSERT_EQ(static_cast<size_t>(NumTabs), web_contents().size());
+ ASSERT_EQ(static_cast<size_t>(num_tabs), web_contents().size());
// Make sure that contents are loaded in the correct order, ie. each tab rank
// is higher that its preceding one.
@@ -1406,3 +1426,94 @@ IN_PROC_BROWSER_TEST_F(SmartSessionRestoreTest, CorrectLoadingOrder) {
// here to avoid a crash.
window.tabs.clear();
}
+
+IN_PROC_BROWSER_TEST_F(SmartSessionRestoreMRUTest, CorrectLoadingOrder) {
+ const int num_tabs = 6;
+
+ Profile* profile = browser()->profile();
+
+ GURL urls[] = {GURL("http://google.com/1"),
+ GURL("http://google.com/2"),
+ GURL("http://google.com/3"),
+ GURL("http://google.com/4"),
+ GURL("http://google.com/5"),
+ GURL("http://google.com/6")};
+
+ int activation_order[] = {4, 2, 1, 5, 0, 3};
+ int activation_order2[] = {4, 2, 5, 0, 3, 1};
+
+ // Replace the first tab and add the other tabs.
+ ui_test_utils::NavigateToURL(browser(), urls[0]);
+ for (int i = 1; i < num_tabs; i++) {
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), urls[i], NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ }
+
+ ASSERT_EQ(num_tabs, browser()->tab_strip_model()->count());
+
+ // Activate the tabs one by one following the random activation order.
+ for (auto i : activation_order)
+ browser()->tab_strip_model()->ActivateTabAt(i, true);
+
+ // Close the browser.
+ g_browser_process->AddRefModule();
+ CloseBrowserSynchronously(browser());
+
+ StartObserving(num_tabs);
+
+ // Create a new window, which should trigger session restore.
+ ui_test_utils::BrowserAddedObserver window_observer;
+ chrome::NewEmptyWindow(profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
+ Browser* new_browser = window_observer.WaitForSingleNewBrowser();
+ ASSERT_TRUE(new_browser != NULL);
+ WaitForAllTabsToStartLoading();
+ g_browser_process->ReleaseModule();
+
+ ASSERT_EQ(static_cast<size_t>(num_tabs), web_contents().size());
+ // Test that we have observed the tabs being loaded in the inverse order of
+ // their activation (MRU). Also validate that their last active time is in the
+ // correct order.
+ for (size_t i = 0; i < web_contents().size(); i++) {
+ GURL expected_url = urls[activation_order[num_tabs - i - 1]];
+ ASSERT_EQ(expected_url, web_contents()[i]->GetLastCommittedURL());
+ if (i > 0) {
+ ASSERT_GT(web_contents()[i - 1]->GetLastActiveTime(),
+ web_contents()[i]->GetLastActiveTime());
+ }
+ }
+
+ // Activate the 2nd tab then close the browser and open it again, to trigger
+ // another session restore. The goal is to ensure that activation time is
+ // persisted between session restores.
+
+ new_browser->tab_strip_model()->ActivateTabAt(1, true);
+
+ // Close the browser.
+ g_browser_process->AddRefModule();
+ CloseBrowserSynchronously(new_browser);
+
+ StartObserving(num_tabs);
+
+ // Create a new window, which should trigger session restore.
+ ui_test_utils::BrowserAddedObserver window_observer2;
+ chrome::NewEmptyWindow(profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
+ Browser* new_browser2 = window_observer2.WaitForSingleNewBrowser();
+ ASSERT_TRUE(new_browser2 != NULL);
+ WaitForAllTabsToStartLoading();
+ g_browser_process->ReleaseModule();
+
+ ASSERT_EQ(static_cast<size_t>(num_tabs), web_contents().size());
+
+ // Test that we have observed the tabs being loaded in the inverse order of
+ // their activation (MRU). Also validate that their last active time is in the
+ // correct order.
+ for (size_t i = 0; i < web_contents().size(); i++) {
+ GURL expected_url = urls[activation_order2[num_tabs - i - 1]];
+ ASSERT_EQ(expected_url, web_contents()[i]->GetLastCommittedURL());
+ if (i > 0) {
+ ASSERT_GT(web_contents()[i - 1]->GetLastActiveTime(),
+ web_contents()[i]->GetLastActiveTime());
+ }
+ }
+}
diff --git a/chrome/browser/sessions/session_restore_delegate.cc b/chrome/browser/sessions/session_restore_delegate.cc
index 9d5c312..52564f9 100644
--- a/chrome/browser/sessions/session_restore_delegate.cc
+++ b/chrome/browser/sessions/session_restore_delegate.cc
@@ -57,7 +57,11 @@ bool SessionRestoreDelegate::RestoredTab::operator<(
// Apps should be loaded before normal tabs.
if (is_app_ != right.is_app_)
return is_app_;
- // TODO(georgesak): Add criterion based on recency.
+ // Restore using MRU. Behind an experiment for now.
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU)
+ return contents_->GetLastActiveTime() >
+ right.contents_->GetLastActiveTime();
return false;
}
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 99de720..0b8759a 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -487,6 +487,16 @@ void SessionService::SetTabExtensionAppID(
tab_id, extension_app_id).Pass());
}
+void SessionService::SetLastActiveTime(const SessionID& window_id,
+ const SessionID& tab_id,
+ base::TimeTicks last_active_time) {
+ if (!ShouldTrackChangesToWindow(window_id))
+ return;
+
+ ScheduleCommand(
+ sessions::CreateLastActiveTimeCommand(tab_id, last_active_time).Pass());
+}
+
base::CancelableTaskTracker::TaskId SessionService::GetLastSession(
const SessionCallback& callback,
base::CancelableTaskTracker* tracker) {
@@ -727,6 +737,13 @@ void SessionService::BuildCommandsForTab(const SessionID& window_id,
sessions::CreatePinnedStateCommand(session_id, true));
}
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU) {
+ base_session_service_->AppendRebuildCommand(
+ sessions::CreateLastActiveTimeCommand(session_id,
+ tab->GetLastActiveTime()));
+ }
+
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(tab);
if (extensions_tab_helper->extension_app()) {
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index ca409cf..0b5212d 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -201,6 +201,11 @@ class SessionService : public BaseSessionServiceDelegateImpl,
const SessionID& tab_id,
const std::string& extension_app_id);
+ // Sets the last active time of the tab.
+ void SetLastActiveTime(const SessionID& window_id,
+ const SessionID& tab_id,
+ base::TimeTicks last_active_time);
+
// Callback from GetLastSession.
// The second parameter is the id of the window that was last active.
typedef base::Callback<void(ScopedVector<sessions::SessionWindow>,
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 1133a6f..ff1fedc 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -70,6 +70,7 @@
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/repost_form_warning_controller.h"
#include "chrome/browser/search/search.h"
+#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_service_factory.h"
#include "chrome/browser/sessions/session_tab_helper.h"
@@ -1071,13 +1072,22 @@ void Browser::ActiveTabChanged(WebContents* old_contents,
find_bar_controller_->find_bar()->MoveWindowIfNecessary(gfx::Rect());
}
- // Update sessions. Don't force creation of sessions. If sessions doesn't
- // exist, the change will be picked up by sessions when created.
+ // Update sessions (selected tab index and last active time). Don't force
+ // creation of sessions. If sessions doesn't exist, the change will be picked
+ // up by sessions when created.
SessionService* session_service =
SessionServiceFactory::GetForProfileIfExisting(profile_);
if (session_service && !tab_strip_model_->closing_all()) {
session_service->SetSelectedTabInWindow(session_id(),
tab_strip_model_->active_index());
+ if (SessionRestore::GetSmartRestoreMode() ==
+ SessionRestore::SMART_RESTORE_MODE_MRU) {
+ SessionTabHelper* session_tab_helper =
+ SessionTabHelper::FromWebContents(new_contents);
+ session_service->SetLastActiveTime(session_id(),
+ session_tab_helper->session_id(),
+ base::TimeTicks::Now());
+ }
}
// This needs to be called after notifying SearchDelegate.
diff --git a/components/sessions/session_service_commands.cc b/components/sessions/session_service_commands.cc
index eca2b4d..a7f653d 100644
--- a/components/sessions/session_service_commands.cc
+++ b/components/sessions/session_service_commands.cc
@@ -38,6 +38,7 @@ static const SessionCommand::id_type kCommandWindowClosed = 17;
static const SessionCommand::id_type kCommandSetTabUserAgentOverride = 18;
static const SessionCommand::id_type kCommandSessionStorageAssociated = 19;
static const SessionCommand::id_type kCommandSetActiveWindow = 20;
+static const SessionCommand::id_type kCommandLastActiveTime = 21;
namespace {
@@ -89,6 +90,11 @@ struct PinnedStatePayload {
bool pinned_state;
};
+struct LastActiveTimePayload {
+ SessionID::id_type tab_id;
+ int64 last_active_time;
+};
+
// Persisted versions of ui::WindowShowState that are written to disk and can
// never change.
enum PersistedWindowShowState {
@@ -558,6 +564,18 @@ bool CreateTabsAndWindows(const ScopedVector<SessionCommand>& data,
break;
}
+ case kCommandLastActiveTime: {
+ LastActiveTimePayload payload;
+ if (!command->GetPayload(&payload, sizeof(payload))) {
+ DVLOG(1) << "Failed reading command " << command->id();
+ return true;
+ }
+ SessionTab* tab = GetTab(payload.tab_id, tabs);
+ tab->last_active_time =
+ base::TimeTicks::FromInternalValue(payload.last_active_time);
+ break;
+ }
+
default:
// TODO(skuhne): This might call back into a callback handler to extend
// the command set for specific implementations.
@@ -704,6 +722,18 @@ scoped_ptr<SessionCommand> CreateSetActiveWindowCommand(
return command;
}
+scoped_ptr<SessionCommand> CreateLastActiveTimeCommand(
+ const SessionID& tab_id,
+ base::TimeTicks last_active_time) {
+ LastActiveTimePayload payload = {0};
+ payload.tab_id = tab_id.id();
+ payload.last_active_time = last_active_time.ToInternalValue();
+ scoped_ptr<SessionCommand> command(
+ new SessionCommand(kCommandLastActiveTime, sizeof(payload)));
+ memcpy(command->contents(), &payload, sizeof(payload));
+ return command;
+}
+
scoped_ptr<SessionCommand> CreateTabNavigationPathPrunedFromBackCommand(
const SessionID& tab_id,
int count) {
diff --git a/components/sessions/session_service_commands.h b/components/sessions/session_service_commands.h
index 6bf3be8..e1cca36 100644
--- a/components/sessions/session_service_commands.h
+++ b/components/sessions/session_service_commands.h
@@ -78,6 +78,9 @@ SESSIONS_EXPORT scoped_ptr<SessionCommand> CreateSetTabUserAgentOverrideCommand(
SESSIONS_EXPORT scoped_ptr<SessionCommand> CreateSetWindowAppNameCommand(
const SessionID& window_id,
const std::string& app_name);
+SESSIONS_EXPORT scoped_ptr<SessionCommand> CreateLastActiveTimeCommand(
+ const SessionID& tab_id,
+ base::TimeTicks last_active_time);
// Searches for a pending command using |base_session_service| that can be
// replaced with |command|. If one is found, pending command is removed, the
diff --git a/components/sessions/session_types.h b/components/sessions/session_types.h
index a7f0358..c71445e 100644
--- a/components/sessions/session_types.h
+++ b/components/sessions/session_types.h
@@ -97,6 +97,11 @@ struct SESSIONS_EXPORT SessionTab {
// Timestamp for when this tab was last modified.
base::Time timestamp;
+ // Timestamp for when this tab was last activated. As these use TimeTicks,
+ // they should not be compared with one another, unless it's within the same
+ // chrome session.
+ base::TimeTicks last_active_time;
+
std::vector<sessions::SerializedNavigationEntry> navigations;
// For reassociating sessionStorage.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 43d74a2..2a8a92c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1052,6 +1052,10 @@ base::TimeTicks WebContentsImpl::GetLastActiveTime() const {
return last_active_time_;
}
+void WebContentsImpl::SetLastActiveTime(base::TimeTicks last_active_time) {
+ last_active_time_ = last_active_time;
+}
+
void WebContentsImpl::WasShown() {
controller_.SetActive(true);
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index b0ebb5e..d95b2aca 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -272,6 +272,7 @@ class CONTENT_EXPORT WebContentsImpl
bool IsBeingDestroyed() const override;
void NotifyNavigationStateChanged(InvalidateTypes changed_flags) override;
base::TimeTicks GetLastActiveTime() const override;
+ void SetLastActiveTime(base::TimeTicks last_active_time) override;
void WasShown() override;
void WasHidden() override;
bool NeedToFireBeforeUnload() override;
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 62f09a8..86ac675 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -351,9 +351,10 @@ class WebContents : public PageNavigator,
// change.
virtual void NotifyNavigationStateChanged(InvalidateTypes changed_flags) = 0;
- // Get the last time that the WebContents was made active (either when it was
- // created or shown with WasShown()).
+ // Get/Set the last time that the WebContents was made active (either when it
+ // was created or shown with WasShown()).
virtual base::TimeTicks GetLastActiveTime() const = 0;
+ virtual void SetLastActiveTime(base::TimeTicks last_active_time) = 0;
// Invoked when the WebContents becomes shown/hidden.
virtual void WasShown() = 0;