Chromium Code Reviews| Index: chrome/browser/memory/tab_manager.cc |
| diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc |
| index e761e81a7c43a1d31ef1ebd15c33c984e6faa98b..a938037e29fb8d07ca30b07dd8b771463ba6e40e 100644 |
| --- a/chrome/browser/memory/tab_manager.cc |
| +++ b/chrome/browser/memory/tab_manager.cc |
| @@ -84,6 +84,16 @@ const int kRecentTabDiscardIntervalSeconds = 60; |
| // machine was suspended and correct the timing statistics. |
| const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; |
| +// The backgrounded renderer is allowed to be suspended for this duration |
| +// before resume. |
| +constexpr base::TimeDelta kMaxTimeRendererAllowedToBeSuspendedBeforeResume = |
| + base::TimeDelta::FromSeconds(120); |
| + |
| +// When the suspended backgrounded renderer is resumed, the renderer runs |
| +// for this duration. |
| +constexpr base::TimeDelta kSuspendedRendererLengthOfResumption = |
| + base::TimeDelta::FromSeconds(10); |
| + |
| // The time during which a tab is protected from discarding after it stops being |
| // audible. |
| const int kAudioProtectionTimeSeconds = 60; |
| @@ -418,17 +428,24 @@ void TabManager::SetTabAutoDiscardableState(content::WebContents* contents, |
| GetWebContentsData(contents)->SetAutoDiscardableState(state); |
| } |
| +content::WebContents* TabManager::GetWebContentsByContentsId( |
| + int64_t tab_contents_id) { |
| + TabStripModel* model; |
| + int index = FindTabStripModelById(tab_contents_id, &model); |
| + if (index == -1) |
| + return nullptr; |
| + return model->GetWebContentsAt(index); |
| +} |
| + |
| bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) { |
| // A renderer can be suspended if it's not playing media. |
| auto tab_stats = GetUnsortedTabStats(); |
| for (auto& tab : tab_stats) { |
| if (tab.child_process_host_id != render_process_id) |
| continue; |
| - TabStripModel* model; |
| - int index = FindTabStripModelById(tab.tab_contents_id, &model); |
| - if (index == -1) |
| + WebContents* web_contents = GetWebContentsByContentsId(tab.tab_contents_id); |
| + if (!web_contents) |
| return false; |
| - WebContents* web_contents = model->GetWebContentsAt(index); |
| if (IsMediaTab(web_contents)) |
| return false; |
| } |
| @@ -687,6 +704,49 @@ void TabManager::UpdateTimerCallback() { |
| PurgeAndSuspendBackgroundedTabs(); |
| } |
| +bool TabManager::ShouldUpdatePurgeAndSuspendState( |
| + const base::TimeTicks& current_time, |
| + const TabStats& tab, |
| + const base::TimeDelta& purge_and_suspend_threshold, |
| + PurgeAndSuspendState* next_state) { |
| + DCHECK(next_state); |
| + content::WebContents* content = |
| + GetWebContentsByContentsId(tab.tab_contents_id); |
| + if (!content) |
| + return false; |
| + |
| + base::TimeTicks last_modified_time = |
| + GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); |
| + PurgeAndSuspendState state = |
| + GetWebContentsData(content)->GetPurgeAndSuspendState(); |
| + // If the tab was foregrounded, should ignore the returned state. |
| + if (tab.last_active > last_modified_time) |
| + state = BACKGROUNDED; |
|
haraken
2016/10/19 08:57:54
Don't we need to set last_modified_time and WebCon
tasak
2016/10/19 10:17:13
tab.last_active is updated only when the tab is sh
|
| + |
| + auto time_passed = current_time - last_modified_time; |
| + switch (state) { |
| + case BACKGROUNDED: |
| + if (time_passed > purge_and_suspend_threshold) { |
| + *next_state = SUSPENDED; |
| + return true; |
| + } |
| + break; |
| + case RESUMED: |
| + if (time_passed > kSuspendedRendererLengthOfResumption) { |
| + *next_state = SUSPENDED; |
| + return true; |
| + } |
| + break; |
| + case SUSPENDED: |
| + if (time_passed > kMaxTimeRendererAllowedToBeSuspendedBeforeResume) { |
| + *next_state = RESUMED; |
| + return true; |
| + } |
| + break; |
| + } |
| + return false; |
| +} |
| + |
| void TabManager::PurgeAndSuspendBackgroundedTabs() { |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| @@ -700,21 +760,38 @@ void TabManager::PurgeAndSuspendBackgroundedTabs() { |
| } |
| if (purge_and_suspend_time <= 0) |
| return; |
| - auto purge_and_suspend_time_threshold = |
| - NowTicks() - base::TimeDelta::FromSeconds(purge_and_suspend_time); |
| + base::TimeTicks current_time = NowTicks(); |
| + base::TimeDelta purge_and_suspend_threshold = |
| + base::TimeDelta::FromSeconds(purge_and_suspend_time); |
| auto tab_stats = GetUnsortedTabStats(); |
| for (auto& tab : tab_stats) { |
| if (!tab.render_process_host->IsProcessBackgrounded()) |
| continue; |
| + if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| + continue; |
| + |
| // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without |
| // timers for simplicity, so PurgeAndSuspend is called even after the |
| // renderer is purged and suspended once. This should be replaced with |
| // timers if we want necessary and sufficient signals. |
| - if (tab.last_active > purge_and_suspend_time_threshold) |
| + PurgeAndSuspendState state = BACKGROUNDED; |
| + if (!ShouldUpdatePurgeAndSuspendState(current_time, tab, |
| + purge_and_suspend_threshold, &state)) |
| continue; |
| - if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| - continue; |
| - tab.render_process_host->PurgeAndSuspend(); |
| + WebContents* content = GetWebContentsByContentsId(tab.tab_contents_id); |
| + GetWebContentsData(content)->SetPurgeAndSuspendState(state); |
| + switch (state) { |
| + case SUSPENDED: |
| + tab.render_process_host->PurgeAndSuspend(); |
| + if (!task_runner_.get()) |
| + task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
|
haraken
2016/10/19 08:57:54
What is this doing?
tasak
2016/10/19 10:17:13
Removed.
|
| + break; |
| + case RESUMED: |
| + tab.render_process_host->Resume(); |
| + break; |
| + case BACKGROUNDED: |
| + NOTREACHED(); |
| + } |
| } |
| } |