| 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 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 203 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 221 OnMemoryPressure(level); | 204 OnMemoryPressure(level); |
| 222 } | 205 } |
| 223 } | 206 } |
| 224 #endif | 207 #endif |
| 225 // purge-and-suspend param is used for Purge+Suspend finch experiment | 208 // purge-and-suspend param is used for Purge+Suspend finch experiment |
| 226 // in the following way: | 209 // in the following way: |
| 227 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7
PPbk/edit?usp=sharing | 210 // https://docs.google.com/document/d/1hPHkKtXXBTlsZx9s-9U17XC-ofEIzPo9FYbBEc7
PPbk/edit?usp=sharing |
| 228 std::string purge_and_suspend_time = variations::GetVariationParamValue( | 211 std::string purge_and_suspend_time = variations::GetVariationParamValue( |
| 229 "PurgeAndSuspend", "purge-and-suspend-time"); | 212 "PurgeAndSuspend", "purge-and-suspend-time"); |
| 230 unsigned int time_to_first_purge_sec = 0; | 213 unsigned int min_time_to_purge_sec = 0; |
| 231 if (purge_and_suspend_time.empty() || | 214 if (purge_and_suspend_time.empty() || |
| 232 !base::StringToUint(purge_and_suspend_time, &time_to_first_purge_sec)) | 215 !base::StringToUint(purge_and_suspend_time, &min_time_to_purge_sec)) |
| 233 time_to_first_suspension_ = kDefaultTimeToFirstPurge; | 216 min_time_to_purge_ = kDefaultMinTimeToPurge; |
| 234 else | 217 else |
| 235 time_to_first_suspension_ = | 218 min_time_to_purge_ = base::TimeDelta::FromSeconds(min_time_to_purge_sec); |
| 236 base::TimeDelta::FromSeconds(time_to_first_purge_sec); | |
| 237 } | 219 } |
| 238 | 220 |
| 239 void TabManager::Stop() { | 221 void TabManager::Stop() { |
| 240 update_timer_.Stop(); | 222 update_timer_.Stop(); |
| 241 recent_tab_discard_timer_.Stop(); | 223 recent_tab_discard_timer_.Stop(); |
| 242 memory_pressure_listener_.reset(); | 224 memory_pressure_listener_.reset(); |
| 243 } | 225 } |
| 244 | 226 |
| 227 int TabManager::FindTabStripModelById(int64_t target_web_contents_id, |
| 228 TabStripModel** model) const { |
| 229 DCHECK(model); |
| 230 // TODO(tasak): Move this code to a TabStripModel enumeration delegate! |
| 231 if (!test_tab_strip_models_.empty()) { |
| 232 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { |
| 233 TabStripModel* local_model = |
| 234 const_cast<TabStripModel*>(test_tab_strip_models_[i].first); |
| 235 int idx = FindWebContentsById(local_model, target_web_contents_id); |
| 236 if (idx != -1) { |
| 237 *model = local_model; |
| 238 return idx; |
| 239 } |
| 240 } |
| 241 |
| 242 return -1; |
| 243 } |
| 244 |
| 245 for (auto* browser : *BrowserList::GetInstance()) { |
| 246 TabStripModel* local_model = browser->tab_strip_model(); |
| 247 int idx = FindWebContentsById(local_model, target_web_contents_id); |
| 248 if (idx != -1) { |
| 249 *model = local_model; |
| 250 return idx; |
| 251 } |
| 252 } |
| 253 |
| 254 return -1; |
| 255 } |
| 256 |
| 245 TabStatsList TabManager::GetTabStats() const { | 257 TabStatsList TabManager::GetTabStats() const { |
| 246 TabStatsList stats_list(GetUnsortedTabStats()); | 258 TabStatsList stats_list(GetUnsortedTabStats()); |
| 247 | 259 |
| 248 // Sort the collected data so that least desirable to be killed is first, most | 260 // Sort the collected data so that least desirable to be killed is first, most |
| 249 // desirable is last. | 261 // desirable is last. |
| 250 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 262 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 251 | 263 |
| 252 return stats_list; | 264 return stats_list; |
| 253 } | 265 } |
| 254 | 266 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 445 content::WebContents* TabManager::GetWebContentsById( | 457 content::WebContents* TabManager::GetWebContentsById( |
| 446 int64_t tab_contents_id) const { | 458 int64_t tab_contents_id) const { |
| 447 TabStripModel* model = nullptr; | 459 TabStripModel* model = nullptr; |
| 448 int index = FindTabStripModelById(tab_contents_id, &model); | 460 int index = FindTabStripModelById(tab_contents_id, &model); |
| 449 if (index == -1) | 461 if (index == -1) |
| 450 return nullptr; | 462 return nullptr; |
| 451 return model->GetWebContentsAt(index); | 463 return model->GetWebContentsAt(index); |
| 452 } | 464 } |
| 453 | 465 |
| 454 bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const { | 466 bool TabManager::CanSuspendBackgroundedRenderer(int render_process_id) const { |
| 455 // A renderer can be suspended if it's not playing media. | 467 // A renderer can be purged if it's not playing media. |
| 456 auto tab_stats = GetUnsortedTabStats(); | 468 auto tab_stats = GetUnsortedTabStats(); |
| 457 for (auto& tab : tab_stats) { | 469 for (auto& tab : tab_stats) { |
| 458 if (tab.child_process_host_id != render_process_id) | 470 if (tab.child_process_host_id != render_process_id) |
| 459 continue; | 471 continue; |
| 460 WebContents* web_contents = GetWebContentsById(tab.tab_contents_id); | 472 WebContents* web_contents = GetWebContentsById(tab.tab_contents_id); |
| 461 if (!web_contents) | 473 if (!web_contents) |
| 462 return false; | 474 return false; |
| 463 if (IsMediaTab(web_contents)) | 475 if (IsMediaTab(web_contents)) |
| 464 return false; | 476 return false; |
| 465 } | 477 } |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 678 stats.title = contents->GetTitle(); | 690 stats.title = contents->GetTitle(); |
| 679 stats.tab_contents_id = IdFromWebContents(contents); | 691 stats.tab_contents_id = IdFromWebContents(contents); |
| 680 stats_list->push_back(stats); | 692 stats_list->push_back(stats); |
| 681 } | 693 } |
| 682 } | 694 } |
| 683 } | 695 } |
| 684 | 696 |
| 685 // This function is called when |update_timer_| fires. It will adjust the clock | 697 // This function is called when |update_timer_| fires. It will adjust the clock |
| 686 // if needed (if it detects that the machine was asleep) and will fire the stats | 698 // if needed (if it detects that the machine was asleep) and will fire the stats |
| 687 // updating on ChromeOS via the delegate. This function also tries to purge | 699 // updating on ChromeOS via the delegate. This function also tries to purge |
| 688 // cache memory and suspend tabs which becomes and keeps backgrounded for a | 700 // cache memory. |
| 689 // while. | |
| 690 void TabManager::UpdateTimerCallback() { | 701 void TabManager::UpdateTimerCallback() { |
| 691 // If Chrome is shutting down, do not do anything. | 702 // If Chrome is shutting down, do not do anything. |
| 692 if (g_browser_process->IsShuttingDown()) | 703 if (g_browser_process->IsShuttingDown()) |
| 693 return; | 704 return; |
| 694 | 705 |
| 695 if (BrowserList::GetInstance()->empty()) | 706 if (BrowserList::GetInstance()->empty()) |
| 696 return; | 707 return; |
| 697 | 708 |
| 698 // Check for a discontinuity in time caused by the machine being suspended. | |
| 699 if (!last_adjust_time_.is_null()) { | |
| 700 TimeDelta suspend_time = NowTicks() - last_adjust_time_; | |
| 701 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { | |
| 702 // System was probably suspended, move the event timers forward in time so | |
| 703 // when they get subtracted out later, "uptime" is being counted. | |
| 704 start_time_ += suspend_time; | |
| 705 if (!last_discard_time_.is_null()) | |
| 706 last_discard_time_ += suspend_time; | |
| 707 } | |
| 708 } | |
| 709 last_adjust_time_ = NowTicks(); | 709 last_adjust_time_ = NowTicks(); |
| 710 | 710 |
| 711 #if defined(OS_CHROMEOS) | 711 #if defined(OS_CHROMEOS) |
| 712 TabStatsList stats_list = GetTabStats(); | 712 TabStatsList stats_list = GetTabStats(); |
| 713 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. | 713 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. |
| 714 delegate_->AdjustOomPriorities(stats_list); | 714 delegate_->AdjustOomPriorities(stats_list); |
| 715 #endif | 715 #endif |
| 716 | 716 |
| 717 PurgeAndSuspendBackgroundedTabs(); | 717 PurgeBackgroundedTabsIfNeeded(); |
| 718 } | 718 } |
| 719 | 719 |
| 720 TabManager::PurgeAndSuspendState TabManager::GetNextPurgeAndSuspendState( | 720 base::TimeDelta TabManager::GetTimeToPurge( |
| 721 content::WebContents* content, | 721 base::TimeDelta min_time_to_purge) const { |
| 722 base::TimeTicks current_time, | 722 return base::TimeDelta::FromSeconds( |
| 723 const base::TimeDelta& time_to_first_suspension) const { | 723 base::RandInt(min_time_to_purge.InSeconds(), |
| 724 DCHECK(content); | 724 min_time_to_purge.InSeconds() * kMinMaxTimeToPurgeRatio)); |
| 725 PurgeAndSuspendState state = | |
| 726 GetWebContentsData(content)->GetPurgeAndSuspendState(); | |
| 727 | |
| 728 auto time_passed = current_time - | |
| 729 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime(); | |
| 730 switch (state) { | |
| 731 case RUNNING: | |
| 732 if (time_passed > time_to_first_suspension) | |
| 733 return SUSPENDED; | |
| 734 break; | |
| 735 case RESUMED: | |
| 736 if (time_passed > kDurationOfRendererResumption) | |
| 737 return SUSPENDED; | |
| 738 break; | |
| 739 case SUSPENDED: | |
| 740 if (time_passed > kDurationOfRendererSuspension) | |
| 741 return RESUMED; | |
| 742 break; | |
| 743 } | |
| 744 return state; | |
| 745 } | 725 } |
| 746 | 726 |
| 747 void TabManager::PurgeAndSuspendBackgroundedTabs() { | 727 bool TabManager::ShouldPurgeNow(content::WebContents* content) const { |
| 748 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() { |
| 749 auto tab_stats = GetUnsortedTabStats(); | 737 auto tab_stats = GetUnsortedTabStats(); |
| 750 for (auto& tab : tab_stats) { | 738 for (auto& tab : tab_stats) { |
| 751 if (!tab.render_process_host->IsProcessBackgrounded()) | 739 if (!tab.render_process_host->IsProcessBackgrounded()) |
| 752 continue; | 740 continue; |
| 753 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) | 741 if (!CanSuspendBackgroundedRenderer(tab.child_process_host_id)) |
| 754 continue; | 742 continue; |
| 755 | 743 |
| 756 WebContents* content = GetWebContentsById(tab.tab_contents_id); | 744 WebContents* content = GetWebContentsById(tab.tab_contents_id); |
| 757 if (!content) | 745 if (!content) |
| 758 continue; | 746 continue; |
| 759 | 747 |
| 760 PurgeAndSuspendState current_state = | 748 bool purge_now = ShouldPurgeNow(content); |
| 761 GetWebContentsData(content)->GetPurgeAndSuspendState(); | 749 if (!purge_now) |
| 762 // If the tab's purge-and-suspend state is not RUNNING, the tab should be | |
| 763 // backgrounded. Since tab.last_hidden is updated everytime the tab is | |
| 764 // hidden, we should see tab.last_hidden < last_modified_time. | |
| 765 DCHECK(current_state == RUNNING || | |
| 766 tab.last_hidden < | |
| 767 GetWebContentsData(content)->LastPurgeAndSuspendModifiedTime()); | |
| 768 PurgeAndSuspendState next_state = GetNextPurgeAndSuspendState( | |
| 769 content, current_time, time_to_first_suspension_); | |
| 770 if (current_state == next_state) | |
| 771 continue; | 750 continue; |
| 772 | 751 |
| 773 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without | 752 // Since |content|'s tab is kept inactive and background for more than |
| 774 // timers for simplicity, so PurgeAndSuspend is called even after the | 753 // time-to-purge time, its purged state changes: false => true. |
| 775 // renderer is purged and suspended once. This should be replaced with | 754 GetWebContentsData(content)->set_is_purged(true); |
| 776 // timers if we want necessary and sufficient signals. | 755 // TODO(tasak): rename PurgeAndSuspend with a better name, e.g. |
| 777 GetWebContentsData(content)->SetPurgeAndSuspendState(next_state); | 756 // RequestPurgeCache, because we don't suspend any renderers. |
| 778 switch (next_state) { | 757 tab.render_process_host->PurgeAndSuspend(); |
| 779 case SUSPENDED: | |
| 780 tab.render_process_host->PurgeAndSuspend(); | |
| 781 break; | |
| 782 case RESUMED: | |
| 783 tab.render_process_host->Resume(); | |
| 784 break; | |
| 785 case RUNNING: | |
| 786 NOTREACHED(); | |
| 787 } | |
| 788 } | 758 } |
| 789 } | 759 } |
| 790 | 760 |
| 791 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 761 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 792 // Can't discard active index. | 762 // Can't discard active index. |
| 793 if (model->active_index() == index) | 763 if (model->active_index() == index) |
| 794 return nullptr; | 764 return nullptr; |
| 795 | 765 |
| 796 WebContents* old_contents = model->GetWebContentsAt(index); | 766 WebContents* old_contents = model->GetWebContentsAt(index); |
| 797 | 767 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 817 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); | 787 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); |
| 818 // Copy over the discard count. | 788 // Copy over the discard count. |
| 819 WebContentsData::CopyState(old_contents, null_contents); | 789 WebContentsData::CopyState(old_contents, null_contents); |
| 820 | 790 |
| 821 // Replace the discarded tab with the null version. | 791 // Replace the discarded tab with the null version. |
| 822 model->ReplaceWebContentsAt(index, null_contents); | 792 model->ReplaceWebContentsAt(index, null_contents); |
| 823 // Mark the tab so it will reload when clicked on. | 793 // Mark the tab so it will reload when clicked on. |
| 824 GetWebContentsData(null_contents)->SetDiscardState(true); | 794 GetWebContentsData(null_contents)->SetDiscardState(true); |
| 825 GetWebContentsData(null_contents)->IncrementDiscardCount(); | 795 GetWebContentsData(null_contents)->IncrementDiscardCount(); |
| 826 | 796 |
| 797 // Make the tab PURGED to avoid purging null_contents. |
| 798 GetWebContentsData(null_contents)->set_is_purged(true); |
| 799 |
| 827 // Discard the old tab's renderer. | 800 // Discard the old tab's renderer. |
| 828 // TODO(jamescook): This breaks script connections with other tabs. | 801 // TODO(jamescook): This breaks script connections with other tabs. |
| 829 // 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 |
| 830 // to swappedout://. | 803 // to swappedout://. |
| 831 delete old_contents; | 804 delete old_contents; |
| 832 recent_tab_discard_ = true; | 805 recent_tab_discard_ = true; |
| 833 | 806 |
| 834 return null_contents; | 807 return null_contents; |
| 835 } | 808 } |
| 836 | 809 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 data->SetRecentlyAudible(current_state); | 851 data->SetRecentlyAudible(current_state); |
| 879 data->SetLastAudioChangeTime(NowTicks()); | 852 data->SetLastAudioChangeTime(NowTicks()); |
| 880 } | 853 } |
| 881 } | 854 } |
| 882 | 855 |
| 883 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | 856 void TabManager::ActiveTabChanged(content::WebContents* old_contents, |
| 884 content::WebContents* new_contents, | 857 content::WebContents* new_contents, |
| 885 int index, | 858 int index, |
| 886 int reason) { | 859 int reason) { |
| 887 GetWebContentsData(new_contents)->SetDiscardState(false); | 860 GetWebContentsData(new_contents)->SetDiscardState(false); |
| 888 GetWebContentsData(new_contents)->SetPurgeAndSuspendState(RUNNING); | 861 // When ActiveTabChanged, |new_contents| purged state changes to be false. |
| 862 GetWebContentsData(new_contents)->set_is_purged(false); |
| 889 // 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 |
| 890 // inactive, so record the time of that transition. | 864 // inactive, so record the time of that transition. |
| 891 if (old_contents) | 865 if (old_contents) { |
| 892 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 } |
| 893 } | 871 } |
| 894 | 872 |
| 895 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, | 873 void TabManager::TabInsertedAt(TabStripModel* tab_strip_model, |
| 896 content::WebContents* contents, | 874 content::WebContents* contents, |
| 897 int index, | 875 int index, |
| 898 bool foreground) { | 876 bool foreground) { |
| 899 // 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 |
| 900 // ActiveTabChanged. | 878 // ActiveTabChanged. |
| 901 if (foreground) | 879 if (foreground) |
| 902 return; | 880 return; |
| 903 | 881 |
| 904 // 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 |
| 905 // inactive. | 883 // inactive. |
| 906 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_)); |
| 907 } | 888 } |
| 908 | 889 |
| 909 bool TabManager::IsMediaTab(WebContents* contents) const { | 890 bool TabManager::IsMediaTab(WebContents* contents) const { |
| 910 if (contents->WasRecentlyAudible()) | 891 if (contents->WasRecentlyAudible()) |
| 911 return true; | 892 return true; |
| 912 | 893 |
| 913 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 894 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
| 914 MediaCaptureDevicesDispatcher::GetInstance() | 895 MediaCaptureDevicesDispatcher::GetInstance() |
| 915 ->GetMediaStreamCaptureIndicator(); | 896 ->GetMediaStreamCaptureIndicator(); |
| 916 if (media_indicator->IsCapturingUserMedia(contents) || | 897 if (media_indicator->IsCapturingUserMedia(contents) || |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1030 // platform. | 1011 // platform. |
| 1031 std::string allow_multiple_discards = variations::GetVariationParamValue( | 1012 std::string allow_multiple_discards = variations::GetVariationParamValue( |
| 1032 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); | 1013 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); |
| 1033 return (allow_multiple_discards != "true"); | 1014 return (allow_multiple_discards != "true"); |
| 1034 #else | 1015 #else |
| 1035 return false; | 1016 return false; |
| 1036 #endif | 1017 #endif |
| 1037 } | 1018 } |
| 1038 | 1019 |
| 1039 } // namespace memory | 1020 } // namespace memory |
| OLD | NEW |