| 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 "ash/multi_profile_uma.h" | 13 #include "ash/multi_profile_uma.h" |
| 14 #include "ash/session/session_state_delegate.h" | 14 #include "ash/session/session_state_delegate.h" |
| 15 #include "ash/shell.h" | 15 #include "ash/shell.h" |
| 16 #include "base/bind.h" | 16 #include "base/bind.h" |
| 17 #include "base/bind_helpers.h" | 17 #include "base/bind_helpers.h" |
| 18 #include "base/command_line.h" | 18 #include "base/command_line.h" |
| 19 #include "base/feature_list.h" | 19 #include "base/feature_list.h" |
| 20 #include "base/macros.h" | 20 #include "base/macros.h" |
| 21 #include "base/memory/memory_pressure_monitor.h" | 21 #include "base/memory/memory_pressure_monitor.h" |
| 22 #include "base/metrics/field_trial.h" | 22 #include "base/metrics/field_trial.h" |
| 23 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
| 24 #include "base/process/process.h" | 24 #include "base/process/process.h" |
| 25 #include "base/strings/string16.h" | 25 #include "base/strings/string16.h" |
| 26 #include "base/strings/string_number_conversions.h" | 26 #include "base/strings/string_number_conversions.h" |
| 27 #include "base/strings/string_util.h" | 27 #include "base/strings/string_util.h" |
| 28 #include "base/strings/utf_string_conversions.h" | 28 #include "base/strings/utf_string_conversions.h" |
| 29 #include "base/thread_task_runner_handle.h" |
| 29 #include "base/threading/thread.h" | 30 #include "base/threading/thread.h" |
| 30 #include "base/time/tick_clock.h" | 31 #include "base/time/tick_clock.h" |
| 31 #include "build/build_config.h" | 32 #include "build/build_config.h" |
| 32 #include "chrome/browser/browser_process.h" | 33 #include "chrome/browser/browser_process.h" |
| 33 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | 34 #include "chrome/browser/media/media_capture_devices_dispatcher.h" |
| 34 #include "chrome/browser/media/media_stream_capture_indicator.h" | 35 #include "chrome/browser/media/media_stream_capture_indicator.h" |
| 35 #include "chrome/browser/memory/oom_memory_details.h" | 36 #include "chrome/browser/memory/oom_memory_details.h" |
| 36 #include "chrome/browser/memory/tab_manager_web_contents_data.h" | 37 #include "chrome/browser/memory/tab_manager_web_contents_data.h" |
| 37 #include "chrome/browser/profiles/profile.h" | 38 #include "chrome/browser/profiles/profile.h" |
| 38 #include "chrome/browser/ui/browser.h" | 39 #include "chrome/browser/ui/browser.h" |
| 39 #include "chrome/browser/ui/browser_iterator.h" | 40 #include "chrome/browser/ui/browser_iterator.h" |
| 40 #include "chrome/browser/ui/browser_list.h" | 41 #include "chrome/browser/ui/browser_list.h" |
| 41 #include "chrome/browser/ui/host_desktop.h" | 42 #include "chrome/browser/ui/host_desktop.h" |
| 42 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 43 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
| 43 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 44 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 44 #include "chrome/browser/ui/tabs/tab_utils.h" | 45 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 45 #include "chrome/common/chrome_constants.h" | 46 #include "chrome/common/chrome_constants.h" |
| 46 #include "chrome/common/chrome_features.h" | 47 #include "chrome/common/chrome_features.h" |
| 47 #include "chrome/common/url_constants.h" | 48 #include "chrome/common/url_constants.h" |
| 48 #include "components/metrics/system_memory_stats_recorder.h" | 49 #include "components/metrics/system_memory_stats_recorder.h" |
| 49 #include "components/variations/variations_associated_data.h" | 50 #include "components/variations/variations_associated_data.h" |
| 50 #include "content/public/browser/browser_thread.h" | 51 #include "content/public/browser/browser_thread.h" |
| 52 #include "content/public/browser/memory_pressure_controller.h" |
| 51 #include "content/public/browser/navigation_controller.h" | 53 #include "content/public/browser/navigation_controller.h" |
| 52 #include "content/public/browser/render_process_host.h" | 54 #include "content/public/browser/render_process_host.h" |
| 53 #include "content/public/browser/web_contents.h" | 55 #include "content/public/browser/web_contents.h" |
| 54 #include "content/public/common/page_importance_signals.h" | 56 #include "content/public/common/page_importance_signals.h" |
| 55 | 57 |
| 56 #if defined(OS_CHROMEOS) | 58 #if defined(OS_CHROMEOS) |
| 57 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" | 59 #include "chrome/browser/memory/tab_manager_delegate_chromeos.h" |
| 58 #endif | 60 #endif |
| 59 | 61 |
| 60 using base::TimeDelta; | 62 using base::TimeDelta; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 100 if (web_contents_id == target_web_contents_id) { | 102 if (web_contents_id == target_web_contents_id) { |
| 101 *model = local_model; | 103 *model = local_model; |
| 102 return idx; | 104 return idx; |
| 103 } | 105 } |
| 104 } | 106 } |
| 105 } | 107 } |
| 106 | 108 |
| 107 return -1; | 109 return -1; |
| 108 } | 110 } |
| 109 | 111 |
| 112 // A wrapper around base::MemoryPressureMonitor::GetCurrentPressureLevel. |
| 113 // TODO(chrisha): Move this do the default implementation of a delegate. |
| 114 base::MemoryPressureListener::MemoryPressureLevel |
| 115 GetCurrentPressureLevel() { |
| 116 auto monitor = base::MemoryPressureMonitor::Get(); |
| 117 if (monitor) |
| 118 return monitor->GetCurrentPressureLevel(); |
| 119 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; |
| 120 } |
| 121 |
| 122 // A wrapper to content::SendPressureNotification that doesn't have overloaded |
| 123 // type ambiguity. Makes use of Bind easier. |
| 124 // TODO(chrisha): Move this do the default implementation of a delegate. |
| 125 void NotifyRendererProcess( |
| 126 const content::RenderProcessHost* render_process_host, |
| 127 base::MemoryPressureListener::MemoryPressureLevel level) { |
| 128 content::MemoryPressureController::SendPressureNotification( |
| 129 render_process_host, level); |
| 130 } |
| 131 |
| 110 } // namespace | 132 } // namespace |
| 111 | 133 |
| 112 //////////////////////////////////////////////////////////////////////////////// | 134 //////////////////////////////////////////////////////////////////////////////// |
| 113 // TabManager | 135 // TabManager |
| 114 | 136 |
| 115 TabManager::TabManager() | 137 TabManager::TabManager() |
| 116 : discard_count_(0), | 138 : discard_count_(0), |
| 117 recent_tab_discard_(false), | 139 recent_tab_discard_(false), |
| 118 discard_once_(false), | 140 discard_once_(false), |
| 119 browser_tab_strip_tracker_(this, nullptr, nullptr), | 141 browser_tab_strip_tracker_(this, nullptr, nullptr), |
| 120 test_tick_clock_(nullptr) { | 142 test_tick_clock_(nullptr), |
| 143 under_memory_pressure_(false), |
| 144 weak_ptr_factory_(this) { |
| 121 #if defined(OS_CHROMEOS) | 145 #if defined(OS_CHROMEOS) |
| 122 delegate_.reset(new TabManagerDelegate); | 146 delegate_.reset(new TabManagerDelegate); |
| 123 #endif | 147 #endif |
| 124 browser_tab_strip_tracker_.Init( | 148 browser_tab_strip_tracker_.Init( |
| 125 BrowserTabStripTracker::InitWith::ALL_BROWERS); | 149 BrowserTabStripTracker::InitWith::ALL_BROWERS); |
| 150 |
| 151 // Set up default callbacks. These may be overridden post-construction as |
| 152 // testing seams. |
| 153 get_current_pressure_level_ = base::Bind(&GetCurrentPressureLevel); |
| 154 notify_renderer_process_ = base::Bind(&NotifyRendererProcess); |
| 126 } | 155 } |
| 127 | 156 |
| 128 TabManager::~TabManager() { | 157 TabManager::~TabManager() { |
| 129 Stop(); | 158 Stop(); |
| 130 } | 159 } |
| 131 | 160 |
| 132 void TabManager::Start() { | 161 void TabManager::Start() { |
| 133 #if defined(OS_WIN) || defined(OS_MACOSX) | 162 #if defined(OS_WIN) || defined(OS_MACOSX) |
| 134 // If the feature is not enabled, do nothing. | 163 // If the feature is not enabled, do nothing. |
| 135 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) | 164 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 } | 225 } |
| 197 | 226 |
| 198 // Things to collect on the browser thread (because TabStripModel isn't thread | 227 // Things to collect on the browser thread (because TabStripModel isn't thread |
| 199 // safe): | 228 // safe): |
| 200 // 1) whether or not a tab is pinned | 229 // 1) whether or not a tab is pinned |
| 201 // 2) last time a tab was selected | 230 // 2) last time a tab was selected |
| 202 // 3) is the tab currently selected | 231 // 3) is the tab currently selected |
| 203 TabStatsList TabManager::GetTabStats() { | 232 TabStatsList TabManager::GetTabStats() { |
| 204 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 233 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 205 TabStatsList stats_list; | 234 TabStatsList stats_list; |
| 206 stats_list.reserve(32); // 99% of users have < 30 tabs open | 235 stats_list.reserve(32); // 99% of users have < 30 tabs open. |
| 207 | 236 |
| 208 // Go through each window to get all the tabs. | 237 // TODO(chrisha): Move this code to a TabStripModel enumeration delegate! |
| 209 AddTabStats(&stats_list); | 238 if (!test_tab_strip_models_.empty()) { |
| 239 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { |
| 240 AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model |
| 241 test_tab_strip_models_[i].second, // is_app |
| 242 i == 0, // is_active |
| 243 &stats_list); |
| 244 } |
| 245 } else { |
| 246 // The code here can only be tested under a full browser test. |
| 247 AddTabStats(&stats_list); |
| 248 } |
| 210 | 249 |
| 211 // Sort the collected data so that least desirable to be killed is first, most | 250 // Sort the collected data so that least desirable to be killed is first, most |
| 212 // desirable is last. | 251 // desirable is last. |
| 213 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 252 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 214 return stats_list; | 253 return stats_list; |
| 215 } | 254 } |
| 216 | 255 |
| 256 std::vector<content::RenderProcessHost*> TabManager::GetOrderedRenderers() { |
| 257 // Get the tab stats. |
| 258 auto tab_stats = GetTabStats(); |
| 259 |
| 260 std::vector<content::RenderProcessHost*> sorted_renderers; |
| 261 std::set<content::RenderProcessHost*> seen_renderers; |
| 262 sorted_renderers.reserve(tab_stats.size()); |
| 263 |
| 264 // Convert the tab sort order to a process sort order. The process inherits |
| 265 // the priority of its highest priority tab. |
| 266 for (auto& tab : tab_stats) { |
| 267 // Skip renderers that have already been encountered. This can occur when |
| 268 // multiple tabs are folded into a single renderer process. In this case the |
| 269 // process takes the priority of its highest priority contained tab. |
| 270 auto renderer = tab.render_process_host; |
| 271 if (!seen_renderers.insert(renderer).second) |
| 272 continue; |
| 273 |
| 274 sorted_renderers.push_back(renderer); |
| 275 } |
| 276 |
| 277 return sorted_renderers; |
| 278 } |
| 279 |
| 217 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { | 280 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { |
| 218 return GetWebContentsData(contents)->IsDiscarded(); | 281 return GetWebContentsData(contents)->IsDiscarded(); |
| 219 } | 282 } |
| 220 | 283 |
| 221 // TODO(jamescook): This should consider tabs with references to other tabs, | 284 // TODO(jamescook): This should consider tabs with references to other tabs, |
| 222 // such as tabs created with JavaScript window.open(). Potentially consider | 285 // such as tabs created with JavaScript window.open(). Potentially consider |
| 223 // discarding the entire set together, or use that in the priority computation. | 286 // discarding the entire set together, or use that in the priority computation. |
| 224 bool TabManager::DiscardTab() { | 287 bool TabManager::DiscardTab() { |
| 225 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 288 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 226 TabStatsList stats = GetTabStats(); | 289 TabStatsList stats = GetTabStats(); |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 400 | 463 |
| 401 void TabManager::AddTabStats(TabStatsList* stats_list) { | 464 void TabManager::AddTabStats(TabStatsList* stats_list) { |
| 402 BrowserList* browser_list = BrowserList::GetInstance(); | 465 BrowserList* browser_list = BrowserList::GetInstance(); |
| 403 // The first window will be the active one. | 466 // The first window will be the active one. |
| 404 bool browser_active = true; | 467 bool browser_active = true; |
| 405 for (BrowserList::const_reverse_iterator browser_iterator = | 468 for (BrowserList::const_reverse_iterator browser_iterator = |
| 406 browser_list->begin_last_active(); | 469 browser_list->begin_last_active(); |
| 407 browser_iterator != browser_list->end_last_active(); | 470 browser_iterator != browser_list->end_last_active(); |
| 408 ++browser_iterator) { | 471 ++browser_iterator) { |
| 409 Browser* browser = *browser_iterator; | 472 Browser* browser = *browser_iterator; |
| 410 bool is_browser_for_app = browser->is_app(); | 473 AddTabStats(browser->tab_strip_model(), browser->is_app(), browser_active, |
| 411 const TabStripModel* model = browser->tab_strip_model(); | 474 stats_list); |
| 412 for (int i = 0; i < model->count(); i++) { | |
| 413 WebContents* contents = model->GetWebContentsAt(i); | |
| 414 if (!contents->IsCrashed()) { | |
| 415 TabStats stats; | |
| 416 stats.is_app = is_browser_for_app; | |
| 417 stats.is_internal_page = | |
| 418 IsInternalPage(contents->GetLastCommittedURL()); | |
| 419 stats.is_media = IsMediaTab(contents); | |
| 420 stats.is_pinned = model->IsTabPinned(i); | |
| 421 stats.is_selected = browser_active && model->IsTabSelected(i); | |
| 422 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | |
| 423 stats.has_form_entry = | |
| 424 contents->GetPageImportanceSignals().had_form_interaction; | |
| 425 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | |
| 426 stats.last_active = contents->GetLastActiveTime(); | |
| 427 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | |
| 428 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | |
| 429 #if defined(OS_CHROMEOS) | |
| 430 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
| 431 #endif | |
| 432 stats.title = contents->GetTitle(); | |
| 433 stats.tab_contents_id = IdFromWebContents(contents); | |
| 434 stats_list->push_back(stats); | |
| 435 } | |
| 436 } | |
| 437 // The active browser window is processed in the first iteration. | 475 // The active browser window is processed in the first iteration. |
| 438 browser_active = false; | 476 browser_active = false; |
| 439 } | 477 } |
| 440 } | 478 } |
| 441 | 479 |
| 480 void TabManager::AddTabStats(const TabStripModel* model, |
| 481 bool is_app, |
| 482 bool active_model, |
| 483 TabStatsList* stats_list) { |
| 484 for (int i = 0; i < model->count(); i++) { |
| 485 WebContents* contents = model->GetWebContentsAt(i); |
| 486 if (!contents->IsCrashed()) { |
| 487 TabStats stats; |
| 488 stats.is_app = is_app; |
| 489 stats.is_internal_page = |
| 490 IsInternalPage(contents->GetLastCommittedURL()); |
| 491 stats.is_media = IsMediaTab(contents); |
| 492 stats.is_pinned = model->IsTabPinned(i); |
| 493 stats.is_selected = active_model && model->IsTabSelected(i); |
| 494 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); |
| 495 stats.has_form_entry = |
| 496 contents->GetPageImportanceSignals().had_form_interaction; |
| 497 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); |
| 498 stats.last_active = contents->GetLastActiveTime(); |
| 499 stats.render_process_host = contents->GetRenderProcessHost(); |
| 500 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| 501 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); |
| 502 #if defined(OS_CHROMEOS) |
| 503 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); |
| 504 #endif |
| 505 stats.title = contents->GetTitle(); |
| 506 stats.tab_contents_id = IdFromWebContents(contents); |
| 507 stats_list->push_back(stats); |
| 508 } |
| 509 } |
| 510 } |
| 511 |
| 442 // This function is called when |update_timer_| fires. It will adjust the clock | 512 // This function is called when |update_timer_| fires. It will adjust the clock |
| 443 // if needed (if it detects that the machine was asleep) and will fire the stats | 513 // if needed (if it detects that the machine was asleep) and will fire the stats |
| 444 // updating on ChromeOS via the delegate. | 514 // updating on ChromeOS via the delegate. |
| 445 void TabManager::UpdateTimerCallback() { | 515 void TabManager::UpdateTimerCallback() { |
| 446 // If Chrome is shutting down, do not do anything. | 516 // If Chrome is shutting down, do not do anything. |
| 447 if (g_browser_process->IsShuttingDown()) | 517 if (g_browser_process->IsShuttingDown()) |
| 448 return; | 518 return; |
| 449 | 519 |
| 450 if (BrowserList::GetInstance()->empty()) | 520 if (BrowserList::GetInstance()->empty()) |
| 451 return; | 521 return; |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 567 | 637 |
| 568 return null_contents; | 638 return null_contents; |
| 569 } | 639 } |
| 570 | 640 |
| 571 void TabManager::OnMemoryPressure( | 641 void TabManager::OnMemoryPressure( |
| 572 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 642 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
| 573 // If Chrome is shutting down, do not do anything. | 643 // If Chrome is shutting down, do not do anything. |
| 574 if (g_browser_process->IsShuttingDown()) | 644 if (g_browser_process->IsShuttingDown()) |
| 575 return; | 645 return; |
| 576 | 646 |
| 577 // For the moment only do something when critical state is reached. | 647 // If no task runner has been set, then use the same one that the memory |
| 648 // pressure subsystem uses. |
| 649 if (!task_runner_.get()) |
| 650 task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| 651 |
| 652 // Under critical pressure try to discard a tab. |
| 578 if (memory_pressure_level == | 653 if (memory_pressure_level == |
| 579 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 654 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 580 LogMemoryAndDiscardTab(); | 655 LogMemoryAndDiscardTab(); |
| 581 } | 656 } |
| 582 // TODO(skuhne): If more memory pressure levels are introduced, consider | 657 // TODO(skuhne): If more memory pressure levels are introduced, consider |
| 583 // calling PurgeBrowserMemory() before CRITICAL is reached. | 658 // calling PurgeBrowserMemory() before CRITICAL is reached. |
| 659 |
| 660 // If this is the beginning of a period of memory pressure then kick off |
| 661 // notification of child processes. |
| 662 // NOTE: This mechanism relies on having a MemoryPressureMonitor |
| 663 // implementation that supports "CurrentPressureLevel". This is true on all |
| 664 // platforms on which TabManager is used. |
| 665 if (!under_memory_pressure_) |
| 666 DoChildProcessDispatch(); |
| 584 } | 667 } |
| 585 | 668 |
| 586 bool TabManager::IsMediaTab(WebContents* contents) const { | 669 bool TabManager::IsMediaTab(WebContents* contents) const { |
| 587 if (contents->WasRecentlyAudible()) | 670 if (contents->WasRecentlyAudible()) |
| 588 return true; | 671 return true; |
| 589 | 672 |
| 590 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 673 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
| 591 MediaCaptureDevicesDispatcher::GetInstance() | 674 MediaCaptureDevicesDispatcher::GetInstance() |
| 592 ->GetMediaStreamCaptureIndicator(); | 675 ->GetMediaStreamCaptureIndicator(); |
| 593 if (media_indicator->IsCapturingUserMedia(contents) || | 676 if (media_indicator->IsCapturingUserMedia(contents) || |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 return first.last_active > second.last_active; | 730 return first.last_active > second.last_active; |
| 648 } | 731 } |
| 649 | 732 |
| 650 TimeTicks TabManager::NowTicks() const { | 733 TimeTicks TabManager::NowTicks() const { |
| 651 if (!test_tick_clock_) | 734 if (!test_tick_clock_) |
| 652 return TimeTicks::Now(); | 735 return TimeTicks::Now(); |
| 653 | 736 |
| 654 return test_tick_clock_->NowTicks(); | 737 return test_tick_clock_->NowTicks(); |
| 655 } | 738 } |
| 656 | 739 |
| 740 void TabManager::DoChildProcessDispatch() { |
| 741 // If Chrome is shutting down, do not do anything. |
| 742 if (g_browser_process->IsShuttingDown()) |
| 743 return; |
| 744 |
| 745 if (!under_memory_pressure_) |
| 746 under_memory_pressure_ = true; |
| 747 |
| 748 // If the memory pressure condition has ended then stop dispatching messages. |
| 749 auto level = get_current_pressure_level_.Run(); |
| 750 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { |
| 751 under_memory_pressure_ = false; |
| 752 notified_renderers_.clear(); |
| 753 return; |
| 754 } |
| 755 |
| 756 // Get a vector of active renderers, from highest to lowest priority. |
| 757 auto renderers = GetOrderedRenderers(); |
| 758 |
| 759 // The following code requires at least one renderer to be present or it will |
| 760 // busyloop. It's possible (however unlikely) for no renderers to exist, so |
| 761 // bail early if that's the case. |
| 762 if (renderers.empty()) |
| 763 return; |
| 764 |
| 765 // Notify a single renderer of memory pressure. |
| 766 bool notified = false; |
| 767 while (!notified) { |
| 768 // Notify the lowest priority renderer that hasn't been notified yet. |
| 769 for (auto rit = renderers.rbegin(); rit != renderers.rend(); ++rit) { |
| 770 // If this renderer has already been notified then look at the next one. |
| 771 if (!notified_renderers_.insert(*rit).second) |
| 772 continue; |
| 773 |
| 774 // Notify the renderer. |
| 775 notify_renderer_process_.Run(*rit, level); |
| 776 notified = true; |
| 777 break; |
| 778 } |
| 779 |
| 780 // If all renderers were processed and none were notified, then all |
| 781 // renderers have already been notified. Clear the list and start again. |
| 782 if (!notified) |
| 783 notified_renderers_.clear(); |
| 784 |
| 785 // This loop can only run at most twice. If it doesn't exit the first time |
| 786 // through, by the second time through |notified_renderers_| will be empty. |
| 787 // Since |renderers| is always non-empty, the first renderer encountered |
| 788 // during the second pass will be notified. |
| 789 } |
| 790 |
| 791 // Schedule another notification. Use a weak pointer so this doesn't explode |
| 792 // during tear down. |
| 793 task_runner_->PostDelayedTask( |
| 794 FROM_HERE, |
| 795 base::Bind(&TabManager::DoChildProcessDispatch, |
| 796 weak_ptr_factory_.GetWeakPtr()), |
| 797 base::TimeDelta::FromSeconds(kRendererNotificationDelayInSeconds)); |
| 798 } |
| 799 |
| 657 } // namespace memory | 800 } // namespace memory |
| OLD | NEW |