Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/memory/tab_manager.h" | 5 #include "chrome/browser/memory/tab_manager.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <set> | 10 #include <set> |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/bind.h" | 13 #include "base/bind.h" |
| 14 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/feature_list.h" | 16 #include "base/feature_list.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/memory/memory_pressure_monitor.h" | 18 #include "base/memory/memory_pressure_monitor.h" |
| 19 #include "base/metrics/field_trial.h" | 19 #include "base/metrics/field_trial.h" |
| 20 #include "base/metrics/histogram_macros.h" | 20 #include "base/metrics/histogram_macros.h" |
| 21 #include "base/observer_list.h" | 21 #include "base/observer_list.h" |
| 22 #include "base/process/process.h" | 22 #include "base/process/process.h" |
| 23 #include "base/rand_util.h" | |
| 23 #include "base/strings/string16.h" | 24 #include "base/strings/string16.h" |
| 24 #include "base/strings/string_number_conversions.h" | 25 #include "base/strings/string_number_conversions.h" |
| 25 #include "base/strings/string_util.h" | 26 #include "base/strings/string_util.h" |
| 26 #include "base/strings/utf_string_conversions.h" | 27 #include "base/strings/utf_string_conversions.h" |
| 27 #include "base/threading/thread.h" | 28 #include "base/threading/thread.h" |
| 28 #include "base/threading/thread_task_runner_handle.h" | 29 #include "base/threading/thread_task_runner_handle.h" |
| 29 #include "base/time/tick_clock.h" | 30 #include "base/time/tick_clock.h" |
| 30 #include "build/build_config.h" | 31 #include "build/build_config.h" |
| 31 #include "chrome/browser/browser_process.h" | 32 #include "chrome/browser/browser_process.h" |
| 32 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" | 33 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 // value. | 74 // value. |
| 74 const int kAdjustmentIntervalSeconds = 10; | 75 const int kAdjustmentIntervalSeconds = 10; |
| 75 | 76 |
| 76 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) | 77 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| 77 // For each period of this length record a statistic to indicate whether or not | 78 // For each period of this length record a statistic to indicate whether or not |
| 78 // the user experienced a low memory event. If this interval is changed, | 79 // the user experienced a low memory event. If this interval is changed, |
| 79 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic. | 80 // Tabs.Discard.DiscardInLastMinute must be replaced with a new statistic. |
| 80 const int kRecentTabDiscardIntervalSeconds = 60; | 81 const int kRecentTabDiscardIntervalSeconds = 60; |
| 81 #endif | 82 #endif |
| 82 | 83 |
| 83 // If there has been no priority adjustment in this interval, assume the | |
| 84 // machine was suspended and correct the timing statistics. | |
| 85 const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; | |
| 86 | |
| 87 // A suspended renderer is suspended for this duration. | |
| 88 constexpr base::TimeDelta kDurationOfRendererSuspension = | |
| 89 base::TimeDelta::FromSeconds(1200); | |
| 90 | |
| 91 // A resumed renderer is resumed for this duration. | |
| 92 constexpr base::TimeDelta kDurationOfRendererResumption = | |
| 93 base::TimeDelta::FromSeconds(10); | |
| 94 | |
| 95 // The time during which a tab is protected from discarding after it stops being | 84 // The time during which a tab is protected from discarding after it stops being |
| 96 // audible. | 85 // audible. |
| 97 const int kAudioProtectionTimeSeconds = 60; | 86 const int kAudioProtectionTimeSeconds = 60; |
| 98 | 87 |
| 99 int FindTabStripModelById(int64_t target_web_contents_id, | 88 int FindWebContentsById(const TabStripModel* model, |
| 100 TabStripModel** model) { | 89 int64_t target_web_contents_id) { |
| 101 DCHECK(model); | 90 for (int idx = 0; idx < model->count(); idx++) { |
| 102 for (auto* browser : *BrowserList::GetInstance()) { | 91 WebContents* web_contents = model->GetWebContentsAt(idx); |
| 103 TabStripModel* local_model = browser->tab_strip_model(); | 92 int64_t web_contents_id = TabManager::IdFromWebContents(web_contents); |
| 104 for (int idx = 0; idx < local_model->count(); idx++) { | 93 if (web_contents_id == target_web_contents_id) |
| 105 WebContents* web_contents = local_model->GetWebContentsAt(idx); | 94 return idx; |
| 106 int64_t web_contents_id = TabManager::IdFromWebContents(web_contents); | |
| 107 if (web_contents_id == target_web_contents_id) { | |
| 108 *model = local_model; | |
| 109 return idx; | |
| 110 } | |
| 111 } | |
| 112 } | 95 } |
| 113 | 96 |
| 114 return -1; | 97 return -1; |
| 115 } | 98 } |
| 116 | 99 |
| 117 // A wrapper around base::MemoryPressureMonitor::GetCurrentPressureLevel. | 100 // A wrapper around base::MemoryPressureMonitor::GetCurrentPressureLevel. |
| 118 // TODO(chrisha): Move this do the default implementation of a delegate. | 101 // TODO(chrisha): Move this do the default implementation of a delegate. |
| 119 base::MemoryPressureListener::MemoryPressureLevel | 102 base::MemoryPressureListener::MemoryPressureLevel |
| 120 GetCurrentPressureLevel() { | 103 GetCurrentPressureLevel() { |
| 121 auto* monitor = base::MemoryPressureMonitor::Get(); | 104 auto* monitor = base::MemoryPressureMonitor::Get(); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 132 base::MemoryPressureListener::MemoryPressureLevel level) { | 115 base::MemoryPressureListener::MemoryPressureLevel level) { |
| 133 content::MemoryPressureController::SendPressureNotification( | 116 content::MemoryPressureController::SendPressureNotification( |
| 134 render_process_host, level); | 117 render_process_host, level); |
| 135 } | 118 } |
| 136 | 119 |
| 137 } // namespace | 120 } // namespace |
| 138 | 121 |
| 139 //////////////////////////////////////////////////////////////////////////////// | 122 //////////////////////////////////////////////////////////////////////////////// |
| 140 // TabManager | 123 // TabManager |
| 141 | 124 |
| 142 constexpr base::TimeDelta TabManager::kDefaultTimeToFirstPurge; | 125 constexpr base::TimeDelta TabManager::kDefaultMinTimeToPurge; |
| 143 | 126 |
| 144 TabManager::TabManager() | 127 TabManager::TabManager() |
| 145 : discard_count_(0), | 128 : discard_count_(0), |
| 146 recent_tab_discard_(false), | 129 recent_tab_discard_(false), |
| 147 discard_once_(false), | 130 discard_once_(false), |
| 148 #if !defined(OS_CHROMEOS) | 131 #if !defined(OS_CHROMEOS) |
| 149 minimum_protection_time_(base::TimeDelta::FromMinutes(10)), | 132 minimum_protection_time_(base::TimeDelta::FromMinutes(10)), |
| 150 #endif | 133 #endif |
| 151 browser_tab_strip_tracker_(this, nullptr, nullptr), | 134 browser_tab_strip_tracker_(this, nullptr, nullptr), |
| 152 test_tick_clock_(nullptr), | 135 test_tick_clock_(nullptr), |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 204 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 222 OnMemoryPressure(level); | 205 OnMemoryPressure(level); |
| 223 } | 206 } |
| 224 } | 207 } |
| 225 #endif | 208 #endif |
| 226 // purge-and-suspend param is used for Purge+Suspend finch experiment | 209 // purge-and-suspend param is used for Purge+Suspend finch experiment |
| 227 // in the following way: | 210 // in the following way: |
| 228 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing | 211 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7 PPbk/edit?usp=sharing |
| 229 std::string purge_and_suspend_time = variations::GetVariationParamValue( | 212 std::string purge_and_suspend_time = variations::GetVariationParamValue( |
| 230 "PurgeAndSuspend", "purge-and-suspend-time"); | 213 "PurgeAndSuspend", "purge-and-suspend-time"); |
| 231 unsigned int time_to_first_purge_sec = 0; | 214 unsigned int min_time_to_purge_sec = 0; |
| 232 if (purge_and_suspend_time.empty() || | 215 if (purge_and_suspend_time.empty() || |
| 233 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) | 216 !base::StringToUint(purge_and_suspend_time, &min_time_to_purge_sec)) |
| 234 time_to_first_suspension_ = kDefaultTimeToFirstPurge; | 217 min_time_to_purge_ = kDefaultMinTimeToPurge; |
| 235 else | 218 else |
| 236 time_to_first_suspension_ = | 219 min_time_to_purge_ = base::TimeDelta::FromSeconds(min_time_to_purge_sec); |
| 237 base::TimeDelta::FromSeconds(time_to_first_purge_sec); | |
| 238 } | 220 } |
| 239 | 221 |
| 240 void TabManager::Stop() { | 222 void TabManager::Stop() { |
| 241 update_timer_.Stop(); | 223 update_timer_.Stop(); |
| 242 recent_tab_discard_timer_.Stop(); | 224 recent_tab_discard_timer_.Stop(); |
| 243 memory_pressure_listener_.reset(); | 225 memory_pressure_listener_.reset(); |
| 244 } | 226 } |
| 245 | 227 |
| 228 int TabManager::FindTabStripModelById(int64_t target_web_contents_id, | |
| 229 TabStripModel** model) const { | |
|
tasak
2017/03/06 10:30:19
I made FindTabStripModelById TabManager's method,
Wez
2017/03/08 02:03:55
Acknowledged.
| |
| 230 DCHECK(model); | |
| 231 // TODO(tasak): Move this code to a TabStripModel enumeration delegate! | |
| 232 if (!test_tab_strip_models_.empty()) { | |
| 233 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { | |
| 234 TabStripModel* local_model = | |
| 235 const_cast<TabStripModel*>(test_tab_strip_models_[i].first); | |
| 236 int idx = FindWebContentsById(local_model, target_web_contents_id); | |
| 237 if (idx != -1) { | |
| 238 *model = local_model; | |
| 239 return idx; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 return -1; | |
| 244 } | |
| 245 | |
| 246 for (auto* browser : *BrowserList::GetInstance()) { | |
| 247 TabStripModel* local_model = browser->tab_strip_model(); | |
| 248 int idx = FindWebContentsById(local_model, target_web_contents_id); | |
| 249 if (idx != -1) { | |
| 250 *model = local_model; | |
| 251 return idx; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 return -1; | |
| 256 } | |
| 257 | |
| 246 TabStatsList TabManager::GetTabStats() const { | 258 TabStatsList TabManager::GetTabStats() const { |
| 247 TabStatsList stats_list(GetUnsortedTabStats()); | 259 TabStatsList stats_list(GetUnsortedTabStats()); |
| 248 | 260 |
| 249 // Sort the collected data so that least desirable to be killed is first, most | 261 // Sort the collected data so that least desirable to be killed is first, most |
| 250 // desirable is last. | 262 // desirable is last. |
| 251 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 263 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 252 | 264 |
| 253 return stats_list; | 265 return stats_list; |
| 254 } | 266 } |
| 255 | 267 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 446 content::WebContents* TabManager::GetWebContentsById( | 458 content::WebContents* TabManager::GetWebContentsById( |
| 447 int64_t tab_contents_id) const { | 459 int64_t tab_contents_id) const { |
| 448 TabStripModel* model = nullptr; | 460 TabStripModel* model = nullptr; |
| 449 int index = FindTabStripModelById(tab_contents_id, &model); | 461 int index = FindTabStripModelById(tab_contents_id, &model); |
| 450 if (index == -1) | 462 if (index == -1) |
| 451 return nullptr; | 463 return nullptr; |
| 452 return model->GetWebContentsAt(index); | 464 return model->GetWebContentsAt(index); |
| 453 } | 465 } |
| 454 | 466 |
| 455 bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const { | 467 bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const { |
| 456 // A renderer can be suspended if it's not playing media. | 468 // A renderer can be purged if it's not playing media. |
| 457 auto tab_stats = GetUnsortedTabStats(); | 469 auto tab_stats = GetUnsortedTabStats(); |
| 458 for (auto& tab : tab_stats) { | 470 for (auto& tab : tab_stats) { |
| 459 if (tab.child_process_host_id != render_process_id) | 471 if (tab.child_process_host_id != render_process_id) |
| 460 continue; | 472 continue; |
| 461 WebContents* web_contents = GetWebContentsById(tab.tab_contents_id); | 473 WebContents* web_contents = GetWebContentsById(tab.tab_contents_id); |
| 462 if (!web_contents) | 474 if (!web_contents) |
| 463 return false; | 475 return false; |
| 464 if (IsMediaTab(web_contents)) | 476 if (IsMediaTab(web_contents)) |
| 465 return false; | 477 return false; |
| 466 } | 478 } |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 679 stats.title = contents->GetTitle(); | 691 stats.title = contents->GetTitle(); |
| 680 stats.tab_contents_id = IdFromWebContents(contents); | 692 stats.tab_contents_id = IdFromWebContents(contents); |
| 681 stats_list->push_back(stats); | 693 stats_list->push_back(stats); |
| 682 } | 694 } |
| 683 } | 695 } |
| 684 } | 696 } |
| 685 | 697 |
| 686 // This function is called when |update_timer_| fires. It will adjust the clock | 698 // This function is called when |update_timer_| fires. It will adjust the clock |
| 687 // if needed (if it detects that the machine was asleep) and will fire the stats | 699 // if needed (if it detects that the machine was asleep) and will fire the stats |
| 688 // updating on ChromeOS via the delegate. This function also tries to purge | 700 // updating on ChromeOS via the delegate. This function also tries to purge |
| 689 // cache memory and suspend tabs which becomes and keeps backgrounded for a | 701 // cache memory. |
| 690 // while. | |
| 691 void TabManager::UpdateTimerCallback() { | 702 void TabManager::UpdateTimerCallback() { |
| 692 // If Chrome is shutting down, do not do anything. | 703 // If Chrome is shutting down, do not do anything. |
| 693 if (g_browser_process->IsShuttingDown()) | 704 if (g_browser_process->IsShuttingDown()) |
| 694 return; | 705 return; |
| 695 | 706 |
| 696 if (BrowserList::GetInstance()->empty()) | 707 if (BrowserList::GetInstance()->empty()) |
| 697 return; | 708 return; |
| 698 | 709 |
| 699 // Check for a discontinuity in time caused by the machine being suspended. | |
| 700 if (!last_adjust_time_.is_null()) { | |
| 701 TimeDelta suspend_time = NowTicks() - last_adjust_time_; | |
| 702 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { | |
| 703 // System was probably suspended, move the event timers forward in time so | |
| 704 // when they get subtracted out later, "uptime" is being counted. | |
| 705 start_time_ += suspend_time; | |
| 706 if (!last_discard_time_.is_null()) | |
| 707 last_discard_time_ += suspend_time; | |
| 708 } | |
| 709 } | |
| 710 last_adjust_time_ = NowTicks(); | 710 last_adjust_time_ = NowTicks(); |
| 711 | 711 |
| 712 #if defined(OS_CHROMEOS) | 712 #if defined(OS_CHROMEOS) |
| 713 TabStatsList stats_list = GetTabStats(); | 713 TabStatsList stats_list = GetTabStats(); |
| 714 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. | 714 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. |
| 715 delegate_->AdjustOomPriorities(stats_list); | 715 delegate_->AdjustOomPriorities(stats_list); |
| 716 #endif | 716 #endif |
| 717 | 717 |
| 718 PurgeAndSuspendBackgroundedTabs(); | 718 PurgeBackgroundedTabsIfNeeded(); |
| 719 } | 719 } |
| 720 | 720 |
| 721 TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState( | 721 base::TimeDelta TabManager::GetTimeToPurge( |
| 722 content::WebContents* content, | 722 base::TimeDelta min_time_to_purge) const { |
| 723 base::TimeTicks current_time, | 723 return base::TimeDelta::FromSeconds(base::RandInt( |
| 724 const base::TimeDelta& time_to_first_suspension) const { | 724 min_time_to_purge.InSeconds(), min_time_to_purge.InSeconds() * 2)); |
| 725 DCHECK(content); | |
| 726 PurgeAndSuspendState state = | |
| 727 GetWebContentsData(content)->GetPurgeAndSuspendState(); | |
| 728 | |
| 729 auto time_passed = current_time - | |
| 730 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); | |
| 731 switch (state) { | |
| 732 case RUNNING: | |
| 733 if (time_passed > time_to_first_suspension) | |
| 734 return SUSPENDED; | |
| 735 break; | |
| 736 case RESUMED: | |
| 737 if (time_passed > kDurationOfRendererResumption) | |
| 738 return SUSPENDED; | |
| 739 break; | |
| 740 case SUSPENDED: | |
| 741 if (time_passed > kDurationOfRendererSuspension) | |
| 742 return RESUMED; | |
| 743 break; | |
| 744 } | |
| 745 return state; | |
| 746 } | 725 } |
| 747 | 726 |
| 748 void TabManager::PurgeAndSuspendBackgroundedTabs() { | 727 bool TabManager::ShouldPurgeNow(content::WebContents* content) const { |
| 749 base::TimeTicks current_time = NowTicks(); | 728 if (GetWebContentsData(content)->is_purged()) |
| 729 return false; | |
| 730 | |
| 731 base::TimeDelta time_passed = | |
| 732 NowTicks() - GetWebContentsData(content)->LastInactiveTime(); | |
| 733 return time_passed > GetWebContentsData(content)->time_to_purge(); | |
| 734 } | |
| 735 | |
| 736 void TabManager::PurgeBackgroundedTabsIfNeeded() { | |
| 750 auto tab_stats = GetUnsortedTabStats(); | 737 auto tab_stats = GetUnsortedTabStats(); |
| 751 for (auto& tab : tab_stats) { | 738 for (auto& tab : tab_stats) { |
| 752 if (!tab.render_process_host->IsProcessBackgrounded()) | 739 if (!tab.render_process_host->IsProcessBackgrounded()) |
| 753 continue; | 740 continue; |
| 754 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) | 741 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| 755 continue; | 742 continue; |
| 756 | 743 |
| 757 WebContents* content = GetWebContentsById(tab.tab_contents_id); | 744 WebContents* content = GetWebContentsById(tab.tab_contents_id); |
| 758 if (!content) | 745 if (!content) |
| 759 continue; | 746 continue; |
| 760 | 747 |
| 761 PurgeAndSuspendState current_state = | 748 bool purge_now = ShouldPurgeNow(content); |
| 762 GetWebContentsData(content)->GetPurgeAndSuspendState(); | 749 if (!purge_now) |
| 763 // If the tab's purge-and-suspend state is not RUNNING, the tab should be | |
| 764 // backgrounded. Since tab.last_hidden is updated everytime the tab is | |
| 765 // hidden, we should see tab.last_hidden < last_modified_time. | |
| 766 DCHECK(current_state == RUNNING || | |
| 767 tab.last_hidden < | |
| 768 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime()); | |
| 769 PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState( | |
| 770 content, current_time, time_to_first_suspension_); | |
| 771 if (current_state == next_state) | |
| 772 continue; | 750 continue; |
| 773 | 751 |
| 774 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without | 752 // Since |content|'s tab is kept inactive and background for more than |
| 775 // timers for simplicity, so PurgeAndSuspend is called even after the | 753 // time-to-purge time, its purged state changes: false => true. |
| 776 // renderer is purged and suspended once. This should be replaced with | 754 GetWebContentsData(content)->set_is_purged(true); |
| 777 // timers if we want necessary and sufficient signals. | 755 // TODO(tasak): rename PurgeAndSuspend with a better name, e.g. |
| 778 GetWebContentsData(content)->SetPurgeAndSuspendState(next_state); | 756 // RequestPurgeCache, because we don't suspend any renderers. |
| 779 switch (next_state) { | 757 tab.render_process_host->PurgeAndSuspend(); |
| 780 case SUSPENDED: | |
| 781 tab.render_process_host->PurgeAndSuspend(); | |
| 782 break; | |
| 783 case RESUMED: | |
| 784 tab.render_process_host->Resume(); | |
| 785 break; | |
| 786 case RUNNING: | |
| 787 NOTREACHED(); | |
| 788 } | |
| 789 } | 758 } |
| 790 } | 759 } |
| 791 | 760 |
| 792 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 761 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 793 // Can't discard active index. | 762 // Can't discard active index. |
| 794 if (model->active_index() == index) | 763 if (model->active_index() == index) |
| 795 return nullptr; | 764 return nullptr; |
| 796 | 765 |
| 797 WebContents* old_contents = model->GetWebContentsAt(index); | 766 WebContents* old_contents = model->GetWebContentsAt(index); |
| 798 | 767 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 818 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); | 787 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); |
| 819 // Copy over the discard count. | 788 // Copy over the discard count. |
| 820 WebContentsData::CopyState(old_contents, null_contents); | 789 WebContentsData::CopyState(old_contents, null_contents); |
| 821 | 790 |
| 822 // Replace the discarded tab with the null version. | 791 // Replace the discarded tab with the null version. |
| 823 model->ReplaceWebContentsAt(index, null_contents); | 792 model->ReplaceWebContentsAt(index, null_contents); |
| 824 // Mark the tab so it will reload when clicked on. | 793 // Mark the tab so it will reload when clicked on. |
| 825 GetWebContentsData(null_contents)->SetDiscardState(true); | 794 GetWebContentsData(null_contents)->SetDiscardState(true); |
| 826 GetWebContentsData(null_contents)->IncrementDiscardCount(); | 795 GetWebContentsData(null_contents)->IncrementDiscardCount(); |
| 827 | 796 |
| 797 // Make the tab PURGED to avoid purging null_contents. | |
| 798 GetWebContentsData(null_contents)->set_is_purged(true); | |
| 799 | |
| 828 // Discard the old tab's renderer. | 800 // Discard the old tab's renderer. |
| 829 // TODO(jamescook): This breaks script connections with other tabs. | 801 // TODO(jamescook): This breaks script connections with other tabs. |
| 830 // Find a different approach that doesn't do that, perhaps based on navigation | 802 // Find a different approach that doesn't do that, perhaps based on navigation |
| 831 // to swappedout://. | 803 // to swappedout://. |
| 832 delete old_contents; | 804 delete old_contents; |
| 833 recent_tab_discard_ = true; | 805 recent_tab_discard_ = true; |
| 834 | 806 |
| 835 return null_contents; | 807 return null_contents; |
| 836 } | 808 } |
| 837 | 809 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 879 data->SetRecentlyAudible(current_state); | 851 data->SetRecentlyAudible(current_state); |
| 880 data->SetLastAudioChangeTime(NowTicks()); | 852 data->SetLastAudioChangeTime(NowTicks()); |
| 881 } | 853 } |
| 882 } | 854 } |
| 883 | 855 |
| 884 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | 856 void TabManager::ActiveTabChanged(content::WebContents* old_contents, |
| 885 content::WebContents* new_contents, | 857 content::WebContents* new_contents, |
| 886 int index, | 858 int index, |
| 887 int reason) { | 859 int reason) { |
| 888 GetWebContentsData(new_contents)->SetDiscardState(false); | 860 GetWebContentsData(new_contents)->SetDiscardState(false); |
| 889 GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); | 861 // When ActiveTabChaged, |new_contents| purged state changes to be false. |
| 862 GetWebContentsData(new_contents)->set_is_purged(false); | |
| 890 // If |old_contents| is set, that tab has switched from being active to | 863 // If |old_contents| is set, that tab has switched from being active to |
| 891 // inactive, so record the time of that transition. | 864 // inactive, so record the time of that transition. |
| 892 if (old_contents) | 865 if (old_contents) { |
| 893 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); | 866 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); |
| 867 // Re-setting time-to-purge every time a tab becomes inactive. | |
| 868 GetWebContentsData(old_contents) | |
| 869 ->set_time_to_purge(GetTimeToPurge(min_time_to_purge_)); | |
| 870 } | |
| 894 } | 871 } |
| 895 | 872 |
| 896 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, | 873 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, |
| 897 content::WebContents* contents, | 874 content::WebContents* contents, |
| 898 int index, | 875 int index, |
| 899 bool foreground) { | 876 bool foreground) { |
| 900 // Only interested in background tabs, as foreground tabs get taken care of by | 877 // Only interested in background tabs, as foreground tabs get taken care of by |
| 901 // ActiveTabChanged. | 878 // ActiveTabChanged. |
| 902 if (foreground) | 879 if (foreground) |
| 903 return; | 880 return; |
| 904 | 881 |
| 905 // A new background tab is similar to having a tab switch from being active to | 882 // A new background tab is similar to having a tab switch from being active to |
| 906 // inactive. | 883 // inactive. |
| 907 GetWebContentsData(contents)->SetLastInactiveTime(NowTicks()); | 884 GetWebContentsData(contents)->SetLastInactiveTime(NowTicks()); |
| 885 // Re-setting time-to-purge every time a tab becomes inactive. | |
| 886 GetWebContentsData(contents)->set_time_to_purge( | |
| 887 GetTimeToPurge(min_time_to_purge_)); | |
| 908 } | 888 } |
| 909 | 889 |
| 910 bool TabManager::IsMediaTab(WebContents* contents) const { | 890 bool TabManager::IsMediaTab(WebContents* contents) const { |
| 911 if (contents->WasRecentlyAudible()) | 891 if (contents->WasRecentlyAudible()) |
| 912 return true; | 892 return true; |
| 913 | 893 |
| 914 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 894 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
| 915 MediaCaptureDevicesDispatcher::GetInstance() | 895 MediaCaptureDevicesDispatcher::GetInstance() |
| 916 ->GetMediaStreamCaptureIndicator(); | 896 ->GetMediaStreamCaptureIndicator(); |
| 917 if (media_indicator->IsCapturingUserMedia(contents) || | 897 if (media_indicator->IsCapturingUserMedia(contents) || |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1031 // platform. | 1011 // platform. |
| 1032 std::string allow_multiple_discards = variations::GetVariationParamValue( | 1012 std::string allow_multiple_discards = variations::GetVariationParamValue( |
| 1033 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); | 1013 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); |
| 1034 return (allow_multiple_discards != "true"); | 1014 return (allow_multiple_discards != "true"); |
| 1035 #else | 1015 #else |
| 1036 return false; | 1016 return false; |
| 1037 #endif | 1017 #endif |
| 1038 } | 1018 } |
| 1039 | 1019 |
| 1040 } // namespace memory | 1020 } // namespace memory |
| OLD | NEW |