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 "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.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::SendPressureNotification(render_process_host, level); | |
| 129 } | |
| 130 | |
| 110 } // namespace | 131 } // namespace |
| 111 | 132 |
| 112 //////////////////////////////////////////////////////////////////////////////// | 133 //////////////////////////////////////////////////////////////////////////////// |
| 113 // TabManager | 134 // TabManager |
| 114 | 135 |
| 115 TabManager::TabManager() | 136 TabManager::TabManager() |
| 116 : discard_count_(0), | 137 : discard_count_(0), |
| 117 recent_tab_discard_(false), | 138 recent_tab_discard_(false), |
| 118 discard_once_(false), | 139 discard_once_(false), |
| 119 browser_tab_strip_tracker_(this, nullptr, nullptr), | 140 browser_tab_strip_tracker_(this, nullptr, nullptr), |
| 120 test_tick_clock_(nullptr) { | 141 test_tick_clock_(nullptr), |
| 142 under_memory_pressure_(false), | |
| 143 weak_ptr_factory_(this) { | |
| 121 #if defined(OS_CHROMEOS) | 144 #if defined(OS_CHROMEOS) |
| 122 delegate_.reset(new TabManagerDelegate); | 145 delegate_.reset(new TabManagerDelegate); |
| 123 #endif | 146 #endif |
| 124 browser_tab_strip_tracker_.Init( | 147 browser_tab_strip_tracker_.Init( |
| 125 BrowserTabStripTracker::InitWith::ALL_BROWERS); | 148 BrowserTabStripTracker::InitWith::ALL_BROWERS); |
| 149 | |
| 150 // Set up default callbacks. These may be overridden post-construction as | |
| 151 // testing seams. | |
| 152 get_current_pressure_level_ = base::Bind(&GetCurrentPressureLevel); | |
| 153 notify_renderer_process_ = base::Bind(&NotifyRendererProcess); | |
| 126 } | 154 } |
| 127 | 155 |
| 128 TabManager::~TabManager() { | 156 TabManager::~TabManager() { |
| 129 Stop(); | 157 Stop(); |
| 130 } | 158 } |
| 131 | 159 |
| 132 void TabManager::Start() { | 160 void TabManager::Start() { |
| 133 #if defined(OS_WIN) || defined(OS_MACOSX) | 161 #if defined(OS_WIN) || defined(OS_MACOSX) |
| 134 // If the feature is not enabled, do nothing. | 162 // If the feature is not enabled, do nothing. |
| 135 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) | 163 if (!base::FeatureList::IsEnabled(features::kAutomaticTabDiscarding)) |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 } | 224 } |
| 197 | 225 |
| 198 // Things to collect on the browser thread (because TabStripModel isn't thread | 226 // Things to collect on the browser thread (because TabStripModel isn't thread |
| 199 // safe): | 227 // safe): |
| 200 // 1) whether or not a tab is pinned | 228 // 1) whether or not a tab is pinned |
| 201 // 2) last time a tab was selected | 229 // 2) last time a tab was selected |
| 202 // 3) is the tab currently selected | 230 // 3) is the tab currently selected |
| 203 TabStatsList TabManager::GetTabStats() { | 231 TabStatsList TabManager::GetTabStats() { |
| 204 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 232 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 205 TabStatsList stats_list; | 233 TabStatsList stats_list; |
| 206 stats_list.reserve(32); // 99% of users have < 30 tabs open | 234 stats_list.reserve(32); // 99% of users have < 30 tabs open. |
| 207 | 235 |
| 208 // Go through each window to get all the tabs. Depending on the platform, | 236 // TODO(chrisha): Move this code to a TabStripModel enumeration delegate! |
| 209 // windows are either native or ash or both. The goal is to make sure to go | 237 if (!test_tab_strip_models_.empty()) { |
| 210 // through them all, starting with the active window first (use | 238 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { |
| 211 // chrome::GetActiveDesktop to get the current used type). | 239 AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model |
| 212 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, | 240 test_tab_strip_models_[i].second, // is_app |
| 213 &stats_list); | 241 i == 0, // is_active |
| 214 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { | 242 &stats_list); |
| 215 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | 243 } |
| 216 false, &stats_list); | 244 } else { |
| 217 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { | 245 // The code here can only be tested under a full browser test. |
| 218 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, | 246 |
| 247 // Go through each window to get all the tabs. Depending on the platform, | |
| 248 // windows are either native or ash or both. The goal is to make sure to go | |
| 249 // through them all, starting with the active window first (use | |
| 250 // chrome::GetActiveDesktop to get the current used type). | |
| 251 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, | |
| 219 &stats_list); | 252 &stats_list); |
| 253 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { | |
| 254 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | |
| 255 false, &stats_list); | |
| 256 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { | |
| 257 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), | |
| 258 false, &stats_list); | |
| 259 } | |
| 220 } | 260 } |
| 221 | 261 |
| 222 // Sort the collected data so that least desirable to be killed is first, most | 262 // Sort the collected data so that least desirable to be killed is first, most |
| 223 // desirable is last. | 263 // desirable is last. |
| 224 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 264 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 225 return stats_list; | 265 return stats_list; |
| 226 } | 266 } |
| 227 | 267 |
| 268 std::vector<content::RenderProcessHost*> TabManager::GetOrderedRenderers() { | |
| 269 // Get the tab stats. | |
| 270 auto tab_stats = GetTabStats(); | |
| 271 | |
| 272 std::vector<content::RenderProcessHost*> sorted_renderers; | |
| 273 std::set<content::RenderProcessHost*> seen_renderers; | |
| 274 sorted_renderers.reserve(tab_stats.size()); | |
| 275 | |
| 276 // Convert the tab sort order to a process sort order. The process inherits | |
| 277 // the priority of its highest priority tab. | |
| 278 for (auto& tab : tab_stats) { | |
| 279 // Skip renderers that have already been encountered. This can occur when | |
| 280 // multiple tabs are folded into a single renderer process. In this case the | |
| 281 // process takes the priority of its highest priority contained tab. | |
| 282 auto renderer = tab.render_process_host; | |
| 283 if (!seen_renderers.insert(renderer).second) | |
| 284 continue; | |
| 285 | |
| 286 sorted_renderers.push_back(renderer); | |
| 287 } | |
| 288 | |
| 289 return sorted_renderers; | |
| 290 } | |
| 291 | |
| 228 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { | 292 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { |
| 229 return GetWebContentsData(contents)->IsDiscarded(); | 293 return GetWebContentsData(contents)->IsDiscarded(); |
| 230 } | 294 } |
| 231 | 295 |
| 232 // TODO(jamescook): This should consider tabs with references to other tabs, | 296 // TODO(jamescook): This should consider tabs with references to other tabs, |
| 233 // such as tabs created with JavaScript window.open(). Potentially consider | 297 // such as tabs created with JavaScript window.open(). Potentially consider |
| 234 // discarding the entire set together, or use that in the priority computation. | 298 // discarding the entire set together, or use that in the priority computation. |
| 235 bool TabManager::DiscardTab() { | 299 bool TabManager::DiscardTab() { |
| 236 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 300 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 237 TabStatsList stats = GetTabStats(); | 301 TabStatsList stats = GetTabStats(); |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 // If it's the active desktop, the first window will be the active one. | 479 // If it's the active desktop, the first window will be the active one. |
| 416 // Otherwise, assume no active windows. | 480 // Otherwise, assume no active windows. |
| 417 bool browser_active = active_desktop; | 481 bool browser_active = active_desktop; |
| 418 for (BrowserList::const_reverse_iterator browser_iterator = | 482 for (BrowserList::const_reverse_iterator browser_iterator = |
| 419 browser_list->begin_last_active(); | 483 browser_list->begin_last_active(); |
| 420 browser_iterator != browser_list->end_last_active(); | 484 browser_iterator != browser_list->end_last_active(); |
| 421 ++browser_iterator) { | 485 ++browser_iterator) { |
| 422 Browser* browser = *browser_iterator; | 486 Browser* browser = *browser_iterator; |
| 423 bool is_browser_for_app = browser->is_app(); | 487 bool is_browser_for_app = browser->is_app(); |
| 424 const TabStripModel* model = browser->tab_strip_model(); | 488 const TabStripModel* model = browser->tab_strip_model(); |
| 425 for (int i = 0; i < model->count(); i++) { | 489 AddTabStats(model, is_browser_for_app, active_desktop, stats_list); |
| 426 WebContents* contents = model->GetWebContentsAt(i); | |
| 427 if (!contents->IsCrashed()) { | |
| 428 TabStats stats; | |
| 429 stats.is_app = is_browser_for_app; | |
| 430 stats.is_internal_page = | |
| 431 IsInternalPage(contents->GetLastCommittedURL()); | |
| 432 stats.is_media = IsMediaTab(contents); | |
| 433 stats.is_pinned = model->IsTabPinned(i); | |
| 434 stats.is_selected = browser_active && model->IsTabSelected(i); | |
| 435 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | |
| 436 stats.has_form_entry = | |
| 437 contents->GetPageImportanceSignals().had_form_interaction; | |
| 438 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | |
| 439 stats.last_active = contents->GetLastActiveTime(); | |
| 440 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | |
| 441 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | |
| 442 #if defined(OS_CHROMEOS) | |
| 443 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
| 444 #endif | |
| 445 stats.title = contents->GetTitle(); | |
| 446 stats.tab_contents_id = IdFromWebContents(contents); | |
| 447 stats_list->push_back(stats); | |
| 448 } | |
| 449 } | |
| 450 // The active browser window is processed in the first iteration. | 490 // The active browser window is processed in the first iteration. |
| 451 browser_active = false; | 491 browser_active = false; |
| 452 } | 492 } |
| 453 } | 493 } |
| 454 | 494 |
| 495 void TabManager::AddTabStats(const TabStripModel* model, | |
| 496 bool is_app, | |
| 497 bool active_model, | |
| 498 TabStatsList* stats_list) { | |
| 499 for (int i = 0; i < model->count(); i++) { | |
| 500 WebContents* contents = model->GetWebContentsAt(i); | |
| 501 if (!contents->IsCrashed()) { | |
| 502 TabStats stats; | |
| 503 stats.is_app = is_app; | |
| 504 stats.is_internal_page = | |
| 505 IsInternalPage(contents->GetLastCommittedURL()); | |
| 506 stats.is_media = IsMediaTab(contents); | |
| 507 stats.is_pinned = model->IsTabPinned(i); | |
| 508 stats.is_selected = active_model && model->IsTabSelected(i); | |
| 509 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | |
| 510 stats.has_form_entry = | |
| 511 contents->GetPageImportanceSignals().had_form_interaction; | |
| 512 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | |
| 513 stats.last_active = contents->GetLastActiveTime(); | |
| 514 stats.render_process_host = contents->GetRenderProcessHost(); | |
| 515 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | |
| 516 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | |
| 517 #if defined(OS_CHROMEOS) | |
| 518 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
| 519 #endif | |
| 520 stats.title = contents->GetTitle(); | |
| 521 stats.tab_contents_id = IdFromWebContents(contents); | |
| 522 stats_list->push_back(stats); | |
| 523 } | |
| 524 } | |
| 525 } | |
| 526 | |
| 455 // This function is called when |update_timer_| fires. It will adjust the clock | 527 // This function is called when |update_timer_| fires. It will adjust the clock |
| 456 // if needed (if it detects that the machine was asleep) and will fire the stats | 528 // if needed (if it detects that the machine was asleep) and will fire the stats |
| 457 // updating on ChromeOS via the delegate. | 529 // updating on ChromeOS via the delegate. |
| 458 void TabManager::UpdateTimerCallback() { | 530 void TabManager::UpdateTimerCallback() { |
| 459 // If Chrome is shutting down, do not do anything. | 531 // If Chrome is shutting down, do not do anything. |
| 460 if (g_browser_process->IsShuttingDown()) | 532 if (g_browser_process->IsShuttingDown()) |
| 461 return; | 533 return; |
| 462 | 534 |
| 463 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty() && | 535 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty() && |
| 464 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty()) | 536 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty()) |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 581 | 653 |
| 582 return null_contents; | 654 return null_contents; |
| 583 } | 655 } |
| 584 | 656 |
| 585 void TabManager::OnMemoryPressure( | 657 void TabManager::OnMemoryPressure( |
| 586 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 658 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
| 587 // If Chrome is shutting down, do not do anything. | 659 // If Chrome is shutting down, do not do anything. |
| 588 if (g_browser_process->IsShuttingDown()) | 660 if (g_browser_process->IsShuttingDown()) |
| 589 return; | 661 return; |
| 590 | 662 |
| 591 // For the moment only do something when critical state is reached. | 663 // If no task runner has been set, then use the same one that the memory |
| 664 // pressure subsystem uses. | |
| 665 if (!task_runner_.get()) | |
| 666 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
| 667 | |
| 668 // Under critical pressure try to discard a tab. | |
| 592 if (memory_pressure_level == | 669 if (memory_pressure_level == |
| 593 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 670 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 594 LogMemoryAndDiscardTab(); | 671 LogMemoryAndDiscardTab(); |
| 595 } | 672 } |
| 596 // TODO(skuhne): If more memory pressure levels are introduced, consider | 673 // TODO(skuhne): If more memory pressure levels are introduced, consider |
| 597 // calling PurgeBrowserMemory() before CRITICAL is reached. | 674 // calling PurgeBrowserMemory() before CRITICAL is reached. |
| 675 | |
| 676 // If this is the beginning of a period of memory pressure then kick off | |
| 677 // notification of child processes. | |
| 678 // NOTE: This mechanism relies on having a MemoryPressureMonitor | |
| 679 // implementation that supports "CurrentPressureLevel". This is true on all | |
| 680 // platforms on which TabManager is used. | |
| 681 if (!under_memory_pressure_) | |
| 682 DoChildProcessDispatch(); | |
| 598 } | 683 } |
| 599 | 684 |
| 600 bool TabManager::IsMediaTab(WebContents* contents) const { | 685 bool TabManager::IsMediaTab(WebContents* contents) const { |
| 601 if (contents->WasRecentlyAudible()) | 686 if (contents->WasRecentlyAudible()) |
| 602 return true; | 687 return true; |
| 603 | 688 |
| 604 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 689 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
| 605 MediaCaptureDevicesDispatcher::GetInstance() | 690 MediaCaptureDevicesDispatcher::GetInstance() |
| 606 ->GetMediaStreamCaptureIndicator(); | 691 ->GetMediaStreamCaptureIndicator(); |
| 607 if (media_indicator->IsCapturingUserMedia(contents) || | 692 if (media_indicator->IsCapturingUserMedia(contents) || |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 661 return first.last_active > second.last_active; | 746 return first.last_active > second.last_active; |
| 662 } | 747 } |
| 663 | 748 |
| 664 TimeTicks TabManager::NowTicks() const { | 749 TimeTicks TabManager::NowTicks() const { |
| 665 if (!test_tick_clock_) | 750 if (!test_tick_clock_) |
| 666 return TimeTicks::Now(); | 751 return TimeTicks::Now(); |
| 667 | 752 |
| 668 return test_tick_clock_->NowTicks(); | 753 return test_tick_clock_->NowTicks(); |
| 669 } | 754 } |
| 670 | 755 |
| 756 void TabManager::DoChildProcessDispatch() { | |
| 757 if (!under_memory_pressure_) | |
| 758 under_memory_pressure_ = true; | |
| 759 | |
| 760 // If the memory pressure condition has ended then stop dispatching messages. | |
| 761 auto level = get_current_pressure_level_.Run(); | |
| 762 if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { | |
| 763 under_memory_pressure_ = false; | |
| 764 notified_renderers_.clear(); | |
| 765 return; | |
| 766 } | |
| 767 | |
| 768 // Get a vector of active renderers, from highest to lowest priority. | |
| 769 auto renderers = GetOrderedRenderers(); | |
| 770 CHECK(!renderers.empty()); | |
|
Georges Khalil
2016/01/28 15:41:02
Could this be triggered while shutting down and no
chrisha
2016/01/29 16:17:54
Very good point, as that's a possible race conditi
| |
| 771 | |
| 772 // Notify a single renderer of memory pressure. | |
| 773 bool notified = false; | |
| 774 while (!notified) { | |
| 775 // Notify the lowest priority renderer that hasn't been notified yet. | |
| 776 for (auto rit = renderers.rbegin(); rit != renderers.rend(); ++rit) { | |
| 777 // If this renderer has already been notified then look at the next one. | |
| 778 if (!notified_renderers_.insert(*rit).second) | |
| 779 continue; | |
| 780 | |
| 781 // Notify the renderer. | |
| 782 notify_renderer_process_.Run(*rit, level); | |
| 783 notified = true; | |
| 784 break; | |
| 785 } | |
| 786 | |
| 787 // If all renderers were processed and none were notified, then all | |
| 788 // renderers have already been notified. Clear the list and start again. | |
| 789 if (!notified) | |
| 790 notified_renderers_.clear(); | |
| 791 | |
| 792 // This loop can only run at most twice. If it doesn't exit the first time | |
| 793 // through, by the second time through |notified_renderers_| will be empty. | |
| 794 // Since |renderers| is always non-empty, the first renderer encountered | |
| 795 // during the second pass will be notified. | |
| 796 } | |
| 797 | |
| 798 // Schedule another notification. Use a weak pointer so this doesn't explode | |
| 799 // during tear down. | |
| 800 task_runner_->PostDelayedTask( | |
| 801 FROM_HERE, | |
| 802 base::Bind(&TabManager::DoChildProcessDispatch, | |
| 803 weak_ptr_factory_.GetWeakPtr()), | |
| 804 base::TimeDelta::FromSeconds(kRendererNotificationDelayInSeconds)); | |
| 805 } | |
| 806 | |
| 671 } // namespace memory | 807 } // namespace memory |
| OLD | NEW |