| 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> |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 264 sorted_renderers.push_back(renderer); | 264 sorted_renderers.push_back(renderer); |
| 265 } | 265 } |
| 266 | 266 |
| 267 return sorted_renderers; | 267 return sorted_renderers; |
| 268 } | 268 } |
| 269 | 269 |
| 270 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { | 270 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { |
| 271 return GetWebContentsData(contents)->IsDiscarded(); | 271 return GetWebContentsData(contents)->IsDiscarded(); |
| 272 } | 272 } |
| 273 | 273 |
| 274 bool TabManager::CanDiscardTab(int64_t target_web_contents_id) const { |
| 275 TabStripModel* model; |
| 276 int idx = FindTabStripModelById(target_web_contents_id, &model); |
| 277 |
| 278 if (idx == -1) |
| 279 return false; |
| 280 |
| 281 WebContents* web_contents = model->GetWebContentsAt(idx); |
| 282 |
| 283 // Do not discard tabs that don't have a valid URL (most probably they have |
| 284 // just been opened and dicarding them would lose the URL). |
| 285 // TODO(georgesak): Look into a workaround to be able to kill the tab without |
| 286 // losing the pending navigation. |
| 287 if (!web_contents->GetLastCommittedURL().is_valid() || |
| 288 web_contents->GetLastCommittedURL().is_empty()) { |
| 289 return false; |
| 290 } |
| 291 |
| 292 // Do not discard tabs in which the user has entered text in a form, lest that |
| 293 // state gets lost. |
| 294 if (web_contents->GetPageImportanceSignals().had_form_interaction) |
| 295 return false; |
| 296 |
| 297 // Do not discard tabs that are playing either playing audio or accessing the |
| 298 // microphone or camera as it's too distruptive to the user experience. Note |
| 299 // that tabs that have recently stopped playing audio by at least |
| 300 // |kAudioProtectionTimeSeconds| seconds are protected as well. |
| 301 if (IsMediaTab(web_contents)) |
| 302 return false; |
| 303 |
| 304 // Do not discard PDFs as they might contain entry that is not saved and they |
| 305 // don't remember their scrolling positions. See crbug.com/547286 and |
| 306 // crbug.com/65244. |
| 307 // TODO(georgesak): Remove this workaround when the bugs are fixed. |
| 308 if (web_contents->GetContentsMimeType() == "application/pdf") |
| 309 return false; |
| 310 |
| 311 // Do not discard a previously discarded tab if that's the desired behavior. |
| 312 if (discard_once_ && GetWebContentsData(web_contents)->DiscardCount() > 0) |
| 313 return false; |
| 314 |
| 315 // Do not discard a recently used tab. |
| 316 if (minimum_protection_time_.InSeconds() > 0) { |
| 317 auto delta = |
| 318 NowTicks() - GetWebContentsData(web_contents)->LastInactiveTime(); |
| 319 if (delta < minimum_protection_time_) |
| 320 return false; |
| 321 } |
| 322 |
| 323 // Do not discard a tab that was explicitly disallowed to. |
| 324 if (!IsTabAutoDiscardable(web_contents)) |
| 325 return false; |
| 326 |
| 327 return true; |
| 328 } |
| 329 |
| 274 void TabManager::DiscardTab() { | 330 void TabManager::DiscardTab() { |
| 275 #if defined(OS_CHROMEOS) | 331 #if defined(OS_CHROMEOS) |
| 276 // Call Chrome OS specific low memory handling process. | 332 // Call Chrome OS specific low memory handling process. |
| 277 if (base::FeatureList::IsEnabled(features::kArcMemoryManagement)) { | 333 if (base::FeatureList::IsEnabled(features::kArcMemoryManagement)) { |
| 278 delegate_->LowMemoryKill(GetUnsortedTabStats()); | 334 delegate_->LowMemoryKill(GetUnsortedTabStats()); |
| 279 return; | 335 return; |
| 280 } | 336 } |
| 281 #endif | 337 #endif |
| 282 DiscardTabImpl(); | 338 DiscardTabImpl(); |
| 283 } | 339 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 309 void TabManager::LogMemory(const std::string& title, | 365 void TabManager::LogMemory(const std::string& title, |
| 310 const base::Closure& callback) { | 366 const base::Closure& callback) { |
| 311 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 367 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 312 OomMemoryDetails::Log(title, callback); | 368 OomMemoryDetails::Log(title, callback); |
| 313 } | 369 } |
| 314 | 370 |
| 315 void TabManager::set_test_tick_clock(base::TickClock* test_tick_clock) { | 371 void TabManager::set_test_tick_clock(base::TickClock* test_tick_clock) { |
| 316 test_tick_clock_ = test_tick_clock; | 372 test_tick_clock_ = test_tick_clock; |
| 317 } | 373 } |
| 318 | 374 |
| 319 void TabManager::TabChangedAt(content::WebContents* contents, | 375 // Things to collect on the browser thread (because TabStripModel isn't thread |
| 320 int index, | 376 // safe): |
| 321 TabChangeType change_type) { | 377 // 1) whether or not a tab is pinned |
| 322 if (change_type != TabChangeType::ALL) | 378 // 2) last time a tab was selected |
| 323 return; | 379 // 3) is the tab currently selected |
| 324 auto* data = GetWebContentsData(contents); | 380 TabStatsList TabManager::GetUnsortedTabStats() { |
| 325 bool old_state = data->IsRecentlyAudible(); | 381 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 326 bool current_state = contents->WasRecentlyAudible(); | 382 TabStatsList stats_list; |
| 327 if (old_state != current_state) { | 383 stats_list.reserve(32); // 99% of users have < 30 tabs open. |
| 328 data->SetRecentlyAudible(current_state); | 384 |
| 329 data->SetLastAudioChangeTime(NowTicks()); | 385 // TODO(chrisha): Move this code to a TabStripModel enumeration delegate! |
| 386 if (!test_tab_strip_models_.empty()) { |
| 387 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { |
| 388 AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model |
| 389 test_tab_strip_models_[i].second, // is_app |
| 390 i == 0, // is_active |
| 391 &stats_list); |
| 392 } |
| 393 } else { |
| 394 // The code here can only be tested under a full browser test. |
| 395 AddTabStats(&stats_list); |
| 330 } | 396 } |
| 397 |
| 398 return stats_list; |
| 331 } | 399 } |
| 332 | 400 |
| 333 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | 401 void TabManager::AddObserver(TabManagerObserver* observer) { |
| 334 content::WebContents* new_contents, | 402 observers_.AddObserver(observer); |
| 335 int index, | |
| 336 int reason) { | |
| 337 GetWebContentsData(new_contents)->SetDiscardState(false); | |
| 338 // If |old_contents| is set, that tab has switched from being active to | |
| 339 // inactive, so record the time of that transition. | |
| 340 if (old_contents) | |
| 341 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); | |
| 342 } | 403 } |
| 343 | 404 |
| 344 void TabManager::TabInsertedAt(content::WebContents* contents, | 405 void TabManager::RemoveObserver(TabManagerObserver* observer) { |
| 345 int index, | 406 observers_.RemoveObserver(observer); |
| 346 bool foreground) { | 407 } |
| 347 // Only interested in background tabs, as foreground tabs get taken care of by | |
| 348 // ActiveTabChanged. | |
| 349 if (foreground) | |
| 350 return; | |
| 351 | 408 |
| 352 // A new background tab is similar to having a tab switch from being active to | 409 void TabManager::set_minimum_protection_time_for_tests( |
| 353 // inactive. | 410 base::TimeDelta minimum_protection_time) { |
| 354 GetWebContentsData(contents)->SetLastInactiveTime(NowTicks()); | 411 minimum_protection_time_ = minimum_protection_time; |
| 412 } |
| 413 |
| 414 bool TabManager::IsTabAutoDiscardable(content::WebContents* contents) const { |
| 415 return GetWebContentsData(contents)->IsAutoDiscardable(); |
| 416 } |
| 417 |
| 418 void TabManager::SetTabAutoDiscardableState(content::WebContents* contents, |
| 419 bool state) { |
| 420 GetWebContentsData(contents)->SetAutoDiscardableState(state); |
| 421 } |
| 422 |
| 423 // static |
| 424 bool TabManager::CompareTabStats(const TabStats& first, |
| 425 const TabStats& second) { |
| 426 // Being currently selected is most important to protect. |
| 427 if (first.is_selected != second.is_selected) |
| 428 return first.is_selected; |
| 429 |
| 430 // Non auto-discardable tabs are more important to protect. |
| 431 if (first.is_auto_discardable != second.is_auto_discardable) |
| 432 return !first.is_auto_discardable; |
| 433 |
| 434 // Protect tabs with pending form entries. |
| 435 if (first.has_form_entry != second.has_form_entry) |
| 436 return first.has_form_entry; |
| 437 |
| 438 // Protect streaming audio and video conferencing tabs as these are similar to |
| 439 // active tabs. |
| 440 if (first.is_media != second.is_media) |
| 441 return first.is_media; |
| 442 |
| 443 // Tab with internal web UI like NTP or Settings are good choices to discard, |
| 444 // so protect non-Web UI and let the other conditionals finish the sort. |
| 445 if (first.is_internal_page != second.is_internal_page) |
| 446 return !first.is_internal_page; |
| 447 |
| 448 // Being pinned is important to protect. |
| 449 if (first.is_pinned != second.is_pinned) |
| 450 return first.is_pinned; |
| 451 |
| 452 // Being an app is important too, as it's the only visible surface in the |
| 453 // window and should not be discarded. |
| 454 if (first.is_app != second.is_app) |
| 455 return first.is_app; |
| 456 |
| 457 // TODO(jamescook): Incorporate sudden_termination_allowed into the sort |
| 458 // order. This is currently not done because pages with unload handlers set |
| 459 // sudden_termination_allowed false, and that covers too many common pages |
| 460 // with ad networks and statistics scripts. Ideally check for beforeUnload |
| 461 // handlers, which are likely to present a dialog asking if the user wants to |
| 462 // discard state. crbug.com/123049. |
| 463 |
| 464 // Being more recently active is more important. |
| 465 return first.last_active > second.last_active; |
| 355 } | 466 } |
| 356 | 467 |
| 357 /////////////////////////////////////////////////////////////////////////////// | 468 /////////////////////////////////////////////////////////////////////////////// |
| 358 // TabManager, private: | 469 // TabManager, private: |
| 359 | 470 |
| 471 void TabManager::OnDiscardedStateChange(content::WebContents* contents, |
| 472 bool is_discarded) { |
| 473 FOR_EACH_OBSERVER(TabManagerObserver, observers_, |
| 474 OnDiscardedStateChange(contents, is_discarded)); |
| 475 } |
| 476 |
| 477 void TabManager::OnAutoDiscardableStateChange(content::WebContents* contents, |
| 478 bool is_auto_discardable) { |
| 479 FOR_EACH_OBSERVER( |
| 480 TabManagerObserver, observers_, |
| 481 OnAutoDiscardableStateChange(contents, is_auto_discardable)); |
| 482 } |
| 483 |
| 360 // static | 484 // static |
| 361 void TabManager::PurgeMemoryAndDiscardTab() { | 485 void TabManager::PurgeMemoryAndDiscardTab() { |
| 362 if (g_browser_process && g_browser_process->GetTabManager()) { | 486 if (g_browser_process && g_browser_process->GetTabManager()) { |
| 363 TabManager* manager = g_browser_process->GetTabManager(); | 487 TabManager* manager = g_browser_process->GetTabManager(); |
| 364 manager->PurgeBrowserMemory(); | 488 manager->PurgeBrowserMemory(); |
| 365 manager->DiscardTab(); | 489 manager->DiscardTab(); |
| 366 } | 490 } |
| 367 } | 491 } |
| 368 | 492 |
| 369 // static | 493 // static |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 | 605 |
| 482 void TabManager::AddTabStats(const TabStripModel* model, | 606 void TabManager::AddTabStats(const TabStripModel* model, |
| 483 bool is_app, | 607 bool is_app, |
| 484 bool active_model, | 608 bool active_model, |
| 485 TabStatsList* stats_list) { | 609 TabStatsList* stats_list) { |
| 486 for (int i = 0; i < model->count(); i++) { | 610 for (int i = 0; i < model->count(); i++) { |
| 487 WebContents* contents = model->GetWebContentsAt(i); | 611 WebContents* contents = model->GetWebContentsAt(i); |
| 488 if (!contents->IsCrashed()) { | 612 if (!contents->IsCrashed()) { |
| 489 TabStats stats; | 613 TabStats stats; |
| 490 stats.is_app = is_app; | 614 stats.is_app = is_app; |
| 491 stats.is_internal_page = | 615 stats.is_internal_page = IsInternalPage(contents->GetLastCommittedURL()); |
| 492 IsInternalPage(contents->GetLastCommittedURL()); | |
| 493 stats.is_media = IsMediaTab(contents); | 616 stats.is_media = IsMediaTab(contents); |
| 494 stats.is_pinned = model->IsTabPinned(i); | 617 stats.is_pinned = model->IsTabPinned(i); |
| 495 stats.is_selected = active_model && model->IsTabSelected(i); | 618 stats.is_selected = active_model && model->IsTabSelected(i); |
| 496 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); | 619 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); |
| 497 stats.has_form_entry = | 620 stats.has_form_entry = |
| 498 contents->GetPageImportanceSignals().had_form_interaction; | 621 contents->GetPageImportanceSignals().had_form_interaction; |
| 499 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); | 622 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); |
| 500 stats.last_active = contents->GetLastActiveTime(); | 623 stats.last_active = contents->GetLastActiveTime(); |
| 501 stats.render_process_host = contents->GetRenderProcessHost(); | 624 stats.render_process_host = contents->GetRenderProcessHost(); |
| 502 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | 625 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 568 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without | 691 // TODO(hajimehoshi): Now calling PurgeAndSuspend is implemented without |
| 569 // timers for simplicity, so PurgeAndSuspend is called even after the | 692 // timers for simplicity, so PurgeAndSuspend is called even after the |
| 570 // renderer is purged and suspended once. This should be replaced with | 693 // renderer is purged and suspended once. This should be replaced with |
| 571 // timers if we want necessary and sufficient signals. | 694 // timers if we want necessary and sufficient signals. |
| 572 if (tab.last_active > purge_and_suspend_time_threshold) | 695 if (tab.last_active > purge_and_suspend_time_threshold) |
| 573 continue; | 696 continue; |
| 574 tab.render_process_host->PurgeAndSuspend(); | 697 tab.render_process_host->PurgeAndSuspend(); |
| 575 } | 698 } |
| 576 } | 699 } |
| 577 | 700 |
| 578 bool TabManager::CanDiscardTab(int64_t target_web_contents_id) const { | |
| 579 TabStripModel* model; | |
| 580 int idx = FindTabStripModelById(target_web_contents_id, &model); | |
| 581 | |
| 582 if (idx == -1) | |
| 583 return false; | |
| 584 | |
| 585 WebContents* web_contents = model->GetWebContentsAt(idx); | |
| 586 | |
| 587 // Do not discard tabs that don't have a valid URL (most probably they have | |
| 588 // just been opened and dicarding them would lose the URL). | |
| 589 // TODO(georgesak): Look into a workaround to be able to kill the tab without | |
| 590 // losing the pending navigation. | |
| 591 if (!web_contents->GetLastCommittedURL().is_valid() || | |
| 592 web_contents->GetLastCommittedURL().is_empty()) { | |
| 593 return false; | |
| 594 } | |
| 595 | |
| 596 // Do not discard tabs in which the user has entered text in a form, lest that | |
| 597 // state gets lost. | |
| 598 if (web_contents->GetPageImportanceSignals().had_form_interaction) | |
| 599 return false; | |
| 600 | |
| 601 // Do not discard tabs that are playing either playing audio or accessing the | |
| 602 // microphone or camera as it's too distruptive to the user experience. Note | |
| 603 // that tabs that have recently stopped playing audio by at least | |
| 604 // |kAudioProtectionTimeSeconds| seconds are protected as well. | |
| 605 if (IsMediaTab(web_contents)) | |
| 606 return false; | |
| 607 | |
| 608 // Do not discard PDFs as they might contain entry that is not saved and they | |
| 609 // don't remember their scrolling positions. See crbug.com/547286 and | |
| 610 // crbug.com/65244. | |
| 611 // TODO(georgesak): Remove this workaround when the bugs are fixed. | |
| 612 if (web_contents->GetContentsMimeType() == "application/pdf") | |
| 613 return false; | |
| 614 | |
| 615 // Do not discard a previously discarded tab if that's the desired behavior. | |
| 616 if (discard_once_ && GetWebContentsData(web_contents)->DiscardCount() > 0) | |
| 617 return false; | |
| 618 | |
| 619 // Do not discard a recently used tab. | |
| 620 if (minimum_protection_time_.InSeconds() > 0) { | |
| 621 auto delta = | |
| 622 NowTicks() - GetWebContentsData(web_contents)->LastInactiveTime(); | |
| 623 if (delta < minimum_protection_time_) | |
| 624 return false; | |
| 625 } | |
| 626 | |
| 627 // Do not discard a tab that was explicitly disallowed to. | |
| 628 if (!IsTabAutoDiscardable(web_contents)) | |
| 629 return false; | |
| 630 | |
| 631 return true; | |
| 632 } | |
| 633 | |
| 634 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 701 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 635 // Can't discard active index. | 702 // Can't discard active index. |
| 636 if (model->active_index() == index) | 703 if (model->active_index() == index) |
| 637 return nullptr; | 704 return nullptr; |
| 638 | 705 |
| 639 WebContents* old_contents = model->GetWebContentsAt(index); | 706 WebContents* old_contents = model->GetWebContentsAt(index); |
| 640 | 707 |
| 641 // Can't discard tabs that are already discarded. | 708 // Can't discard tabs that are already discarded. |
| 642 if (GetWebContentsData(old_contents)->IsDiscarded()) | 709 if (GetWebContentsData(old_contents)->IsDiscarded()) |
| 643 return nullptr; | 710 return nullptr; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 702 // implementation that supports "CurrentPressureLevel". This is true on all | 769 // implementation that supports "CurrentPressureLevel". This is true on all |
| 703 // platforms on which TabManager is used. | 770 // platforms on which TabManager is used. |
| 704 #if !defined(OS_CHROMEOS) | 771 #if !defined(OS_CHROMEOS) |
| 705 // Running GC under memory pressure can cause thrashing. Disable it on | 772 // Running GC under memory pressure can cause thrashing. Disable it on |
| 706 // ChromeOS until the thrashing is fixed. crbug.com/588172. | 773 // ChromeOS until the thrashing is fixed. crbug.com/588172. |
| 707 if (!under_memory_pressure_) | 774 if (!under_memory_pressure_) |
| 708 DoChildProcessDispatch(); | 775 DoChildProcessDispatch(); |
| 709 #endif | 776 #endif |
| 710 } | 777 } |
| 711 | 778 |
| 779 void TabManager::TabChangedAt(content::WebContents* contents, |
| 780 int index, |
| 781 TabChangeType change_type) { |
| 782 if (change_type != TabChangeType::ALL) |
| 783 return; |
| 784 auto* data = GetWebContentsData(contents); |
| 785 bool old_state = data->IsRecentlyAudible(); |
| 786 bool current_state = contents->WasRecentlyAudible(); |
| 787 if (old_state != current_state) { |
| 788 data->SetRecentlyAudible(current_state); |
| 789 data->SetLastAudioChangeTime(NowTicks()); |
| 790 } |
| 791 } |
| 792 |
| 793 void TabManager::ActiveTabChanged(content::WebContents* old_contents, |
| 794 content::WebContents* new_contents, |
| 795 int index, |
| 796 int reason) { |
| 797 GetWebContentsData(new_contents)->SetDiscardState(false); |
| 798 // If |old_contents| is set, that tab has switched from being active to |
| 799 // inactive, so record the time of that transition. |
| 800 if (old_contents) |
| 801 GetWebContentsData(old_contents)->SetLastInactiveTime(NowTicks()); |
| 802 } |
| 803 |
| 804 void TabManager::TabInsertedAt(content::WebContents* contents, |
| 805 int index, |
| 806 bool foreground) { |
| 807 // Only interested in background tabs, as foreground tabs get taken care of by |
| 808 // ActiveTabChanged. |
| 809 if (foreground) |
| 810 return; |
| 811 |
| 812 // A new background tab is similar to having a tab switch from being active to |
| 813 // inactive. |
| 814 GetWebContentsData(contents)->SetLastInactiveTime(NowTicks()); |
| 815 } |
| 816 |
| 712 bool TabManager::IsMediaTab(WebContents* contents) const { | 817 bool TabManager::IsMediaTab(WebContents* contents) const { |
| 713 if (contents->WasRecentlyAudible()) | 818 if (contents->WasRecentlyAudible()) |
| 714 return true; | 819 return true; |
| 715 | 820 |
| 716 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = | 821 scoped_refptr<MediaStreamCaptureIndicator> media_indicator = |
| 717 MediaCaptureDevicesDispatcher::GetInstance() | 822 MediaCaptureDevicesDispatcher::GetInstance() |
| 718 ->GetMediaStreamCaptureIndicator(); | 823 ->GetMediaStreamCaptureIndicator(); |
| 719 if (media_indicator->IsCapturingUserMedia(contents) || | 824 if (media_indicator->IsCapturingUserMedia(contents) || |
| 720 media_indicator->IsBeingMirrored(contents)) { | 825 media_indicator->IsBeingMirrored(contents)) { |
| 721 return true; | 826 return true; |
| 722 } | 827 } |
| 723 | 828 |
| 724 auto delta = NowTicks() - GetWebContentsData(contents)->LastAudioChangeTime(); | 829 auto delta = NowTicks() - GetWebContentsData(contents)->LastAudioChangeTime(); |
| 725 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); | 830 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); |
| 726 } | 831 } |
| 727 | 832 |
| 728 TabManager::WebContentsData* TabManager::GetWebContentsData( | 833 TabManager::WebContentsData* TabManager::GetWebContentsData( |
| 729 content::WebContents* contents) const { | 834 content::WebContents* contents) const { |
| 730 WebContentsData::CreateForWebContents(contents); | 835 WebContentsData::CreateForWebContents(contents); |
| 731 auto* web_contents_data = WebContentsData::FromWebContents(contents); | 836 auto* web_contents_data = WebContentsData::FromWebContents(contents); |
| 732 web_contents_data->set_test_tick_clock(test_tick_clock_); | 837 web_contents_data->set_test_tick_clock(test_tick_clock_); |
| 733 return web_contents_data; | 838 return web_contents_data; |
| 734 } | 839 } |
| 735 | 840 |
| 736 // static | |
| 737 bool TabManager::CompareTabStats(const TabStats& first, | |
| 738 const TabStats& second) { | |
| 739 // Being currently selected is most important to protect. | |
| 740 if (first.is_selected != second.is_selected) | |
| 741 return first.is_selected; | |
| 742 | |
| 743 // Non auto-discardable tabs are more important to protect. | |
| 744 if (first.is_auto_discardable != second.is_auto_discardable) | |
| 745 return !first.is_auto_discardable; | |
| 746 | |
| 747 // Protect tabs with pending form entries. | |
| 748 if (first.has_form_entry != second.has_form_entry) | |
| 749 return first.has_form_entry; | |
| 750 | |
| 751 // Protect streaming audio and video conferencing tabs as these are similar to | |
| 752 // active tabs. | |
| 753 if (first.is_media != second.is_media) | |
| 754 return first.is_media; | |
| 755 | |
| 756 // Tab with internal web UI like NTP or Settings are good choices to discard, | |
| 757 // so protect non-Web UI and let the other conditionals finish the sort. | |
| 758 if (first.is_internal_page != second.is_internal_page) | |
| 759 return !first.is_internal_page; | |
| 760 | |
| 761 // Being pinned is important to protect. | |
| 762 if (first.is_pinned != second.is_pinned) | |
| 763 return first.is_pinned; | |
| 764 | |
| 765 // Being an app is important too, as it's the only visible surface in the | |
| 766 // window and should not be discarded. | |
| 767 if (first.is_app != second.is_app) | |
| 768 return first.is_app; | |
| 769 | |
| 770 // TODO(jamescook): Incorporate sudden_termination_allowed into the sort | |
| 771 // order. This is currently not done because pages with unload handlers set | |
| 772 // sudden_termination_allowed false, and that covers too many common pages | |
| 773 // with ad networks and statistics scripts. Ideally check for beforeUnload | |
| 774 // handlers, which are likely to present a dialog asking if the user wants to | |
| 775 // discard state. crbug.com/123049. | |
| 776 | |
| 777 // Being more recently active is more important. | |
| 778 return first.last_active > second.last_active; | |
| 779 } | |
| 780 | |
| 781 TimeTicks TabManager::NowTicks() const { | 841 TimeTicks TabManager::NowTicks() const { |
| 782 if (!test_tick_clock_) | 842 if (!test_tick_clock_) |
| 783 return TimeTicks::Now(); | 843 return TimeTicks::Now(); |
| 784 | 844 |
| 785 return test_tick_clock_->NowTicks(); | 845 return test_tick_clock_->NowTicks(); |
| 786 } | 846 } |
| 787 | 847 |
| 788 void TabManager::DoChildProcessDispatch() { | 848 void TabManager::DoChildProcessDispatch() { |
| 789 // If Chrome is shutting down, do not do anything. | 849 // If Chrome is shutting down, do not do anything. |
| 790 if (g_browser_process->IsShuttingDown()) | 850 if (g_browser_process->IsShuttingDown()) |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 877 // TODO(georgesak): Add Linux when automatic discarding is enabled for that | 937 // TODO(georgesak): Add Linux when automatic discarding is enabled for that |
| 878 // platform. | 938 // platform. |
| 879 std::string allow_multiple_discards = variations::GetVariationParamValue( | 939 std::string allow_multiple_discards = variations::GetVariationParamValue( |
| 880 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); | 940 features::kAutomaticTabDiscarding.name, "AllowMultipleDiscards"); |
| 881 return (allow_multiple_discards != "true"); | 941 return (allow_multiple_discards != "true"); |
| 882 #else | 942 #else |
| 883 return false; | 943 return false; |
| 884 #endif | 944 #endif |
| 885 } | 945 } |
| 886 | 946 |
| 887 // Things to collect on the browser thread (because TabStripModel isn't thread | |
| 888 // safe): | |
| 889 // 1) whether or not a tab is pinned | |
| 890 // 2) last time a tab was selected | |
| 891 // 3) is the tab currently selected | |
| 892 TabStatsList TabManager::GetUnsortedTabStats() { | |
| 893 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 894 TabStatsList stats_list; | |
| 895 stats_list.reserve(32); // 99% of users have < 30 tabs open. | |
| 896 | |
| 897 // TODO(chrisha): Move this code to a TabStripModel enumeration delegate! | |
| 898 if (!test_tab_strip_models_.empty()) { | |
| 899 for (size_t i = 0; i < test_tab_strip_models_.size(); ++i) { | |
| 900 AddTabStats(test_tab_strip_models_[i].first, // tab_strip_model | |
| 901 test_tab_strip_models_[i].second, // is_app | |
| 902 i == 0, // is_active | |
| 903 &stats_list); | |
| 904 } | |
| 905 } else { | |
| 906 // The code here can only be tested under a full browser test. | |
| 907 AddTabStats(&stats_list); | |
| 908 } | |
| 909 | |
| 910 return stats_list; | |
| 911 } | |
| 912 | |
| 913 void TabManager::AddObserver(TabManagerObserver* observer) { | |
| 914 observers_.AddObserver(observer); | |
| 915 } | |
| 916 | |
| 917 void TabManager::RemoveObserver(TabManagerObserver* observer) { | |
| 918 observers_.RemoveObserver(observer); | |
| 919 } | |
| 920 | |
| 921 void TabManager::OnDiscardedStateChange(content::WebContents* contents, | |
| 922 bool is_discarded) { | |
| 923 FOR_EACH_OBSERVER(TabManagerObserver, observers_, | |
| 924 OnDiscardedStateChange(contents, is_discarded)); | |
| 925 } | |
| 926 | |
| 927 void TabManager::set_minimum_protection_time_for_tests( | |
| 928 base::TimeDelta minimum_protection_time) { | |
| 929 minimum_protection_time_ = minimum_protection_time; | |
| 930 } | |
| 931 | |
| 932 void TabManager::OnAutoDiscardableStateChange(content::WebContents* contents, | |
| 933 bool is_auto_discardable) { | |
| 934 FOR_EACH_OBSERVER( | |
| 935 TabManagerObserver, observers_, | |
| 936 OnAutoDiscardableStateChange(contents, is_auto_discardable)); | |
| 937 } | |
| 938 | |
| 939 bool TabManager::IsTabAutoDiscardable(content::WebContents* contents) const { | |
| 940 return GetWebContentsData(contents)->IsAutoDiscardable(); | |
| 941 } | |
| 942 | |
| 943 void TabManager::SetTabAutoDiscardableState(content::WebContents* contents, | |
| 944 bool state) { | |
| 945 GetWebContentsData(contents)->SetAutoDiscardableState(state); | |
| 946 } | |
| 947 | |
| 948 } // namespace memory | 947 } // namespace memory |
| OLD | NEW |