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 <algorithm> | 7 #include <algorithm> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 173 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, | 173 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, |
| 174 &stats_list); | 174 &stats_list); |
| 175 } | 175 } |
| 176 | 176 |
| 177 // Sort the collected data so that least desirable to be killed is first, most | 177 // Sort the collected data so that least desirable to be killed is first, most |
| 178 // desirable is last. | 178 // desirable is last. |
| 179 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 179 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 180 return stats_list; | 180 return stats_list; |
| 181 } | 181 } |
| 182 | 182 |
| 183 bool TabManager::IsTabDiscarded(content::WebContents* contents) const { | |
| 184 return GetWebContentsData(contents)->IsDiscarded(); | |
| 185 } | |
| 186 | |
| 183 // TODO(jamescook): This should consider tabs with references to other tabs, | 187 // TODO(jamescook): This should consider tabs with references to other tabs, |
| 184 // such as tabs created with JavaScript window.open(). Potentially consider | 188 // such as tabs created with JavaScript window.open(). Potentially consider |
| 185 // discarding the entire set together, or use that in the priority computation. | 189 // discarding the entire set together, or use that in the priority computation. |
| 186 bool TabManager::DiscardTab() { | 190 bool TabManager::DiscardTab() { |
| 187 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 191 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 188 TabStatsList stats = GetTabStats(); | 192 TabStatsList stats = GetTabStats(); |
| 189 if (stats.empty()) | 193 if (stats.empty()) |
| 190 return false; | 194 return false; |
| 191 // Loop until a non-discarded tab to kill is found. | 195 // Loop until a non-discarded tab to kill is found. |
| 192 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); | 196 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 218 | 222 |
| 219 void TabManager::LogMemory(const std::string& title, | 223 void TabManager::LogMemory(const std::string& title, |
| 220 const base::Closure& callback) { | 224 const base::Closure& callback) { |
| 221 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 225 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 222 OomMemoryDetails::Log(title, callback); | 226 OomMemoryDetails::Log(title, callback); |
| 223 } | 227 } |
| 224 | 228 |
| 225 void TabManager::TabChangedAt(content::WebContents* contents, | 229 void TabManager::TabChangedAt(content::WebContents* contents, |
| 226 int index, | 230 int index, |
| 227 TabChangeType change_type) { | 231 TabChangeType change_type) { |
| 232 WebContentsData::CreateForWebContents(contents); | |
| 228 if (change_type != TabChangeType::ALL) | 233 if (change_type != TabChangeType::ALL) |
| 229 return; | 234 return; |
| 230 | 235 auto data = GetWebContentsData(contents); |
|
chrisha
2015/10/30 15:24:40
Doesn't this already call create CreateForWebConte
Georges Khalil
2015/10/30 15:30:05
Removed the call to CreateForWebContents at start
| |
| 231 bool old_state = WebContentsData::IsRecentlyAudible(contents); | 236 bool old_state = data->IsRecentlyAudible(); |
| 232 bool current_state = contents->WasRecentlyAudible(); | 237 bool current_state = contents->WasRecentlyAudible(); |
| 233 if (old_state != current_state) { | 238 if (old_state != current_state) { |
| 234 WebContentsData::SetRecentlyAudible(contents, current_state); | 239 data->SetRecentlyAudible(current_state); |
| 235 WebContentsData::SetLastAudioChangeTime(contents, TimeTicks::Now()); | 240 data->SetLastAudioChangeTime(TimeTicks::Now()); |
| 236 } | 241 } |
| 237 } | 242 } |
| 238 | 243 |
| 244 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | |
| 245 content::WebContents* new_contents, | |
| 246 int index, | |
| 247 int reason) { | |
| 248 GetWebContentsData(new_contents)->SetDiscardState(false); | |
| 249 } | |
| 250 | |
| 239 /////////////////////////////////////////////////////////////////////////////// | 251 /////////////////////////////////////////////////////////////////////////////// |
| 240 // TabManager, private: | 252 // TabManager, private: |
| 241 | 253 |
| 242 // static | 254 // static |
| 243 void TabManager::PurgeMemoryAndDiscardTab() { | 255 void TabManager::PurgeMemoryAndDiscardTab() { |
| 244 if (g_browser_process && g_browser_process->GetTabManager()) { | 256 if (g_browser_process && g_browser_process->GetTabManager()) { |
| 245 TabManager* manager = g_browser_process->GetTabManager(); | 257 TabManager* manager = g_browser_process->GetTabManager(); |
| 246 manager->PurgeBrowserMemory(); | 258 manager->PurgeBrowserMemory(); |
| 247 manager->DiscardTab(); | 259 manager->DiscardTab(); |
| 248 } | 260 } |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 for (int i = 0; i < model->count(); i++) { | 373 for (int i = 0; i < model->count(); i++) { |
| 362 WebContents* contents = model->GetWebContentsAt(i); | 374 WebContents* contents = model->GetWebContentsAt(i); |
| 363 if (!contents->IsCrashed()) { | 375 if (!contents->IsCrashed()) { |
| 364 TabStats stats; | 376 TabStats stats; |
| 365 stats.is_app = is_browser_for_app; | 377 stats.is_app = is_browser_for_app; |
| 366 stats.is_internal_page = | 378 stats.is_internal_page = |
| 367 IsInternalPage(contents->GetLastCommittedURL()); | 379 IsInternalPage(contents->GetLastCommittedURL()); |
| 368 stats.is_playing_audio = IsAudioTab(contents); | 380 stats.is_playing_audio = IsAudioTab(contents); |
| 369 stats.is_pinned = model->IsTabPinned(i); | 381 stats.is_pinned = model->IsTabPinned(i); |
| 370 stats.is_selected = browser_active && model->IsTabSelected(i); | 382 stats.is_selected = browser_active && model->IsTabSelected(i); |
| 371 stats.is_discarded = WebContentsData::IsDiscarded(contents); | 383 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); |
| 372 stats.has_form_entry = | 384 stats.has_form_entry = |
| 373 contents->GetPageImportanceSignals().had_form_interaction; | 385 contents->GetPageImportanceSignals().had_form_interaction; |
| 374 stats.discard_count = WebContentsData::DiscardCount(contents); | 386 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); |
| 375 stats.last_active = contents->GetLastActiveTime(); | 387 stats.last_active = contents->GetLastActiveTime(); |
| 376 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | 388 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| 377 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | 389 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); |
| 378 #if defined(OS_CHROMEOS) | 390 #if defined(OS_CHROMEOS) |
| 379 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | 391 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); |
| 380 #endif | 392 #endif |
| 381 stats.title = contents->GetTitle(); | 393 stats.title = contents->GetTitle(); |
| 382 stats.tab_contents_id = IdFromWebContents(contents); | 394 stats.tab_contents_id = IdFromWebContents(contents); |
| 383 stats_list->push_back(stats); | 395 stats_list->push_back(stats); |
| 384 } | 396 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 434 if (web_contents->GetPageImportanceSignals().had_form_interaction) | 446 if (web_contents->GetPageImportanceSignals().had_form_interaction) |
| 435 return false; | 447 return false; |
| 436 | 448 |
| 437 // Do not discard tabs that are playing audio as it's too distruptive to the | 449 // Do not discard tabs that are playing audio as it's too distruptive to the |
| 438 // user experience. Note that tabs that have recently stopped playing audio by | 450 // user experience. Note that tabs that have recently stopped playing audio by |
| 439 // at least |kAudioProtectionTimeSeconds| seconds are protected as well. | 451 // at least |kAudioProtectionTimeSeconds| seconds are protected as well. |
| 440 if (IsAudioTab(web_contents)) | 452 if (IsAudioTab(web_contents)) |
| 441 return false; | 453 return false; |
| 442 | 454 |
| 443 // Do not discard a previously discarded tab if that's the desired behavior. | 455 // Do not discard a previously discarded tab if that's the desired behavior. |
| 444 if (discard_once_ && WebContentsData::DiscardCount(web_contents) > 0) | 456 if (discard_once_ && GetWebContentsData(web_contents)->DiscardCount() > 0) |
| 445 return false; | 457 return false; |
| 446 | 458 |
| 447 return true; | 459 return true; |
| 448 } | 460 } |
| 449 | 461 |
| 450 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 462 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 451 // Can't discard active index. | 463 // Can't discard active index. |
| 452 if (model->active_index() == index) | 464 if (model->active_index() == index) |
| 453 return nullptr; | 465 return nullptr; |
| 454 | 466 |
| 455 WebContents* old_contents = model->GetWebContentsAt(index); | 467 WebContents* old_contents = model->GetWebContentsAt(index); |
| 456 | 468 |
| 457 // Can't discard tabs that are already discarded. | 469 // Can't discard tabs that are already discarded. |
| 458 if (WebContentsData::IsDiscarded(old_contents)) | 470 if (GetWebContentsData(old_contents)->IsDiscarded()) |
| 459 return nullptr; | 471 return nullptr; |
| 460 | 472 |
| 461 // Record statistics before discarding to capture the memory state that leads | 473 // Record statistics before discarding to capture the memory state that leads |
| 462 // to the discard. | 474 // to the discard. |
| 463 RecordDiscardStatistics(); | 475 RecordDiscardStatistics(); |
| 464 | 476 |
| 465 WebContents* null_contents = | 477 WebContents* null_contents = |
| 466 WebContents::Create(WebContents::CreateParams(model->profile())); | 478 WebContents::Create(WebContents::CreateParams(model->profile())); |
| 467 // Copy over the state from the navigation controller to preserve the | 479 // Copy over the state from the navigation controller to preserve the |
| 468 // back/forward history and to continue to display the correct title/favicon. | 480 // back/forward history and to continue to display the correct title/favicon. |
| 469 null_contents->GetController().CopyStateFrom(old_contents->GetController()); | 481 null_contents->GetController().CopyStateFrom(old_contents->GetController()); |
| 470 | 482 |
| 471 // Make sure to persist the last active time property. | 483 // Make sure to persist the last active time property. |
| 472 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); | 484 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); |
| 473 // Copy over the discard count. | 485 // Copy over the discard count. |
| 474 WebContentsData::CopyState(old_contents, null_contents); | 486 WebContentsData::CopyState(old_contents, null_contents); |
| 475 | 487 |
| 476 // Replace the discarded tab with the null version. | 488 // Replace the discarded tab with the null version. |
| 477 model->ReplaceWebContentsAt(index, null_contents); | 489 model->ReplaceWebContentsAt(index, null_contents); |
| 478 // Mark the tab so it will reload when clicked on. | 490 // Mark the tab so it will reload when clicked on. |
| 479 WebContentsData::SetDiscardState(null_contents, true); | 491 GetWebContentsData(null_contents)->SetDiscardState(true); |
| 480 WebContentsData::IncrementDiscardCount(null_contents); | 492 GetWebContentsData(null_contents)->IncrementDiscardCount(); |
| 481 | 493 |
| 482 // Discard the old tab's renderer. | 494 // Discard the old tab's renderer. |
| 483 // TODO(jamescook): This breaks script connections with other tabs. | 495 // TODO(jamescook): This breaks script connections with other tabs. |
| 484 // Find a different approach that doesn't do that, perhaps based on navigation | 496 // Find a different approach that doesn't do that, perhaps based on navigation |
| 485 // to swappedout://. | 497 // to swappedout://. |
| 486 delete old_contents; | 498 delete old_contents; |
| 487 recent_tab_discard_ = true; | 499 recent_tab_discard_ = true; |
| 488 | 500 |
| 489 return null_contents; | 501 return null_contents; |
| 490 } | 502 } |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 501 LogMemoryAndDiscardTab(); | 513 LogMemoryAndDiscardTab(); |
| 502 } | 514 } |
| 503 // TODO(skuhne): If more memory pressure levels are introduced, consider | 515 // TODO(skuhne): If more memory pressure levels are introduced, consider |
| 504 // calling PurgeBrowserMemory() before CRITICAL is reached. | 516 // calling PurgeBrowserMemory() before CRITICAL is reached. |
| 505 } | 517 } |
| 506 | 518 |
| 507 bool TabManager::IsAudioTab(WebContents* contents) const { | 519 bool TabManager::IsAudioTab(WebContents* contents) const { |
| 508 if (contents->WasRecentlyAudible()) | 520 if (contents->WasRecentlyAudible()) |
| 509 return true; | 521 return true; |
| 510 auto delta = | 522 auto delta = |
| 511 TimeTicks::Now() - WebContentsData::LastAudioChangeTime(contents); | 523 TimeTicks::Now() - GetWebContentsData(contents)->LastAudioChangeTime(); |
| 512 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); | 524 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); |
| 513 } | 525 } |
| 514 | 526 |
| 527 TabManager::WebContentsData* TabManager::GetWebContentsData( | |
| 528 content::WebContents* contents) const { | |
| 529 WebContentsData::CreateForWebContents(contents); | |
| 530 return WebContentsData::FromWebContents(contents); | |
| 531 } | |
| 532 | |
| 515 // static | 533 // static |
| 516 bool TabManager::CompareTabStats(TabStats first, TabStats second) { | 534 bool TabManager::CompareTabStats(TabStats first, TabStats second) { |
| 517 // Being currently selected is most important to protect. | 535 // Being currently selected is most important to protect. |
| 518 if (first.is_selected != second.is_selected) | 536 if (first.is_selected != second.is_selected) |
| 519 return first.is_selected; | 537 return first.is_selected; |
| 520 | 538 |
| 521 // Protect tabs with pending form entries. | 539 // Protect tabs with pending form entries. |
| 522 if (first.has_form_entry != second.has_form_entry) | 540 if (first.has_form_entry != second.has_form_entry) |
| 523 return first.has_form_entry; | 541 return first.has_form_entry; |
| 524 | 542 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 546 // sudden_termination_allowed false, and that covers too many common pages | 564 // sudden_termination_allowed false, and that covers too many common pages |
| 547 // with ad networks and statistics scripts. Ideally check for beforeUnload | 565 // with ad networks and statistics scripts. Ideally check for beforeUnload |
| 548 // handlers, which are likely to present a dialog asking if the user wants to | 566 // handlers, which are likely to present a dialog asking if the user wants to |
| 549 // discard state. crbug.com/123049. | 567 // discard state. crbug.com/123049. |
| 550 | 568 |
| 551 // Being more recently active is more important. | 569 // Being more recently active is more important. |
| 552 return first.last_active > second.last_active; | 570 return first.last_active > second.last_active; |
| 553 } | 571 } |
| 554 | 572 |
| 555 } // namespace memory | 573 } // namespace memory |
| OLD | NEW |