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 22 matching lines...) Expand all Loading... | |
| 215 LogMemory("Tab Discards Memory details", | 219 LogMemory("Tab Discards Memory details", |
| 216 base::Bind(&TabManager::PurgeMemoryAndDiscardTab)); | 220 base::Bind(&TabManager::PurgeMemoryAndDiscardTab)); |
| 217 } | 221 } |
| 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 |
| 229 void TabManager::TabInsertedAt(content::WebContents* contents, | |
| 230 int index, | |
| 231 bool foreground) { | |
| 232 WebContentsData::CreateForWebContents(contents); | |
|
sky
2015/10/27 21:16:46
Is this necessary? Doesn't GetWebContentsData() im
Georges Khalil
2015/10/30 14:17:44
Correct, this is not needed. Removed.
| |
| 233 } | |
| 234 | |
| 225 void TabManager::TabChangedAt(content::WebContents* contents, | 235 void TabManager::TabChangedAt(content::WebContents* contents, |
| 226 int index, | 236 int index, |
| 227 TabChangeType change_type) { | 237 TabChangeType change_type) { |
| 238 WebContentsData::CreateForWebContents(contents); | |
| 228 if (change_type != TabChangeType::ALL) | 239 if (change_type != TabChangeType::ALL) |
| 229 return; | 240 return; |
| 230 | 241 auto data = GetWebContentsData(contents); |
| 231 bool old_state = WebContentsData::IsRecentlyAudible(contents); | 242 bool old_state = data->IsRecentlyAudible(); |
| 232 bool current_state = contents->WasRecentlyAudible(); | 243 bool current_state = contents->WasRecentlyAudible(); |
| 233 if (old_state != current_state) { | 244 if (old_state != current_state) { |
| 234 WebContentsData::SetRecentlyAudible(contents, current_state); | 245 data->SetRecentlyAudible(current_state); |
| 235 WebContentsData::SetLastAudioChangeTime(contents, TimeTicks::Now()); | 246 data->SetLastAudioChangeTime(TimeTicks::Now()); |
| 236 } | 247 } |
| 237 } | 248 } |
| 238 | 249 |
| 250 void TabManager::ActiveTabChanged(content::WebContents* old_contents, | |
| 251 content::WebContents* new_contents, | |
| 252 int index, | |
| 253 int reason) { | |
| 254 GetWebContentsData(new_contents)->SetDiscardState(false); | |
| 255 } | |
| 256 | |
| 239 /////////////////////////////////////////////////////////////////////////////// | 257 /////////////////////////////////////////////////////////////////////////////// |
| 240 // TabManager, private: | 258 // TabManager, private: |
| 241 | 259 |
| 242 // static | 260 // static |
| 243 void TabManager::PurgeMemoryAndDiscardTab() { | 261 void TabManager::PurgeMemoryAndDiscardTab() { |
| 244 if (g_browser_process && g_browser_process->GetTabManager()) { | 262 if (g_browser_process && g_browser_process->GetTabManager()) { |
| 245 TabManager* manager = g_browser_process->GetTabManager(); | 263 TabManager* manager = g_browser_process->GetTabManager(); |
| 246 manager->PurgeBrowserMemory(); | 264 manager->PurgeBrowserMemory(); |
| 247 manager->DiscardTab(); | 265 manager->DiscardTab(); |
| 248 } | 266 } |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 for (int i = 0; i < model->count(); i++) { | 379 for (int i = 0; i < model->count(); i++) { |
| 362 WebContents* contents = model->GetWebContentsAt(i); | 380 WebContents* contents = model->GetWebContentsAt(i); |
| 363 if (!contents->IsCrashed()) { | 381 if (!contents->IsCrashed()) { |
| 364 TabStats stats; | 382 TabStats stats; |
| 365 stats.is_app = is_browser_for_app; | 383 stats.is_app = is_browser_for_app; |
| 366 stats.is_internal_page = | 384 stats.is_internal_page = |
| 367 IsInternalPage(contents->GetLastCommittedURL()); | 385 IsInternalPage(contents->GetLastCommittedURL()); |
| 368 stats.is_playing_audio = IsAudioTab(contents); | 386 stats.is_playing_audio = IsAudioTab(contents); |
| 369 stats.is_pinned = model->IsTabPinned(i); | 387 stats.is_pinned = model->IsTabPinned(i); |
| 370 stats.is_selected = browser_active && model->IsTabSelected(i); | 388 stats.is_selected = browser_active && model->IsTabSelected(i); |
| 371 stats.is_discarded = WebContentsData::IsDiscarded(contents); | 389 stats.is_discarded = GetWebContentsData(contents)->IsDiscarded(); |
| 372 stats.has_form_entry = | 390 stats.has_form_entry = |
| 373 contents->GetPageImportanceSignals().had_form_interaction; | 391 contents->GetPageImportanceSignals().had_form_interaction; |
| 374 stats.discard_count = WebContentsData::DiscardCount(contents); | 392 stats.discard_count = GetWebContentsData(contents)->DiscardCount(); |
| 375 stats.last_active = contents->GetLastActiveTime(); | 393 stats.last_active = contents->GetLastActiveTime(); |
| 376 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | 394 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
| 377 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | 395 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); |
| 378 #if defined(OS_CHROMEOS) | 396 #if defined(OS_CHROMEOS) |
| 379 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | 397 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); |
| 380 #endif | 398 #endif |
| 381 stats.title = contents->GetTitle(); | 399 stats.title = contents->GetTitle(); |
| 382 stats.tab_contents_id = IdFromWebContents(contents); | 400 stats.tab_contents_id = IdFromWebContents(contents); |
| 383 stats_list->push_back(stats); | 401 stats_list->push_back(stats); |
| 384 } | 402 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 434 if (web_contents->GetPageImportanceSignals().had_form_interaction) | 452 if (web_contents->GetPageImportanceSignals().had_form_interaction) |
| 435 return false; | 453 return false; |
| 436 | 454 |
| 437 // Do not discard tabs that are playing audio as it's too distruptive to the | 455 // 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 | 456 // user experience. Note that tabs that have recently stopped playing audio by |
| 439 // at least |kAudioProtectionTimeSeconds| seconds are protected as well. | 457 // at least |kAudioProtectionTimeSeconds| seconds are protected as well. |
| 440 if (IsAudioTab(web_contents)) | 458 if (IsAudioTab(web_contents)) |
| 441 return false; | 459 return false; |
| 442 | 460 |
| 443 // Do not discard a previously discarded tab if that's the desired behavior. | 461 // Do not discard a previously discarded tab if that's the desired behavior. |
| 444 if (discard_once_ && WebContentsData::DiscardCount(web_contents) > 0) | 462 if (discard_once_ && GetWebContentsData(web_contents)->DiscardCount() > 0) |
| 445 return false; | 463 return false; |
| 446 | 464 |
| 447 return true; | 465 return true; |
| 448 } | 466 } |
| 449 | 467 |
| 450 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { | 468 WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { |
| 451 // Can't discard active index. | 469 // Can't discard active index. |
| 452 if (model->active_index() == index) | 470 if (model->active_index() == index) |
| 453 return nullptr; | 471 return nullptr; |
| 454 | 472 |
| 455 WebContents* old_contents = model->GetWebContentsAt(index); | 473 WebContents* old_contents = model->GetWebContentsAt(index); |
| 456 | 474 |
| 457 // Can't discard tabs that are already discarded. | 475 // Can't discard tabs that are already discarded. |
| 458 if (WebContentsData::IsDiscarded(old_contents)) | 476 if (GetWebContentsData(old_contents)->IsDiscarded()) |
| 459 return nullptr; | 477 return nullptr; |
| 460 | 478 |
| 461 // Record statistics before discarding to capture the memory state that leads | 479 // Record statistics before discarding to capture the memory state that leads |
| 462 // to the discard. | 480 // to the discard. |
| 463 RecordDiscardStatistics(); | 481 RecordDiscardStatistics(); |
| 464 | 482 |
| 465 WebContents* null_contents = | 483 WebContents* null_contents = |
| 466 WebContents::Create(WebContents::CreateParams(model->profile())); | 484 WebContents::Create(WebContents::CreateParams(model->profile())); |
| 467 // Copy over the state from the navigation controller to preserve the | 485 // Copy over the state from the navigation controller to preserve the |
| 468 // back/forward history and to continue to display the correct title/favicon. | 486 // back/forward history and to continue to display the correct title/favicon. |
| 469 null_contents->GetController().CopyStateFrom(old_contents->GetController()); | 487 null_contents->GetController().CopyStateFrom(old_contents->GetController()); |
| 470 | 488 |
| 471 // Make sure to persist the last active time property. | 489 // Make sure to persist the last active time property. |
| 472 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); | 490 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); |
| 473 // Copy over the discard count. | 491 // Copy over the discard count. |
| 474 WebContentsData::CopyState(old_contents, null_contents); | 492 WebContentsData::CopyState(old_contents, null_contents); |
| 475 | 493 |
| 476 // Replace the discarded tab with the null version. | 494 // Replace the discarded tab with the null version. |
| 477 model->ReplaceWebContentsAt(index, null_contents); | 495 model->ReplaceWebContentsAt(index, null_contents); |
| 478 // Mark the tab so it will reload when clicked on. | 496 // Mark the tab so it will reload when clicked on. |
| 479 WebContentsData::SetDiscardState(null_contents, true); | 497 GetWebContentsData(null_contents)->SetDiscardState(true); |
| 480 WebContentsData::IncrementDiscardCount(null_contents); | 498 GetWebContentsData(null_contents)->IncrementDiscardCount(); |
| 481 | 499 |
| 482 // Discard the old tab's renderer. | 500 // Discard the old tab's renderer. |
| 483 // TODO(jamescook): This breaks script connections with other tabs. | 501 // TODO(jamescook): This breaks script connections with other tabs. |
| 484 // Find a different approach that doesn't do that, perhaps based on navigation | 502 // Find a different approach that doesn't do that, perhaps based on navigation |
| 485 // to swappedout://. | 503 // to swappedout://. |
| 486 delete old_contents; | 504 delete old_contents; |
| 487 recent_tab_discard_ = true; | 505 recent_tab_discard_ = true; |
| 488 | 506 |
| 489 return null_contents; | 507 return null_contents; |
| 490 } | 508 } |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 501 LogMemoryAndDiscardTab(); | 519 LogMemoryAndDiscardTab(); |
| 502 } | 520 } |
| 503 // TODO(skuhne): If more memory pressure levels are introduced, consider | 521 // TODO(skuhne): If more memory pressure levels are introduced, consider |
| 504 // calling PurgeBrowserMemory() before CRITICAL is reached. | 522 // calling PurgeBrowserMemory() before CRITICAL is reached. |
| 505 } | 523 } |
| 506 | 524 |
| 507 bool TabManager::IsAudioTab(WebContents* contents) const { | 525 bool TabManager::IsAudioTab(WebContents* contents) const { |
| 508 if (contents->WasRecentlyAudible()) | 526 if (contents->WasRecentlyAudible()) |
| 509 return true; | 527 return true; |
| 510 auto delta = | 528 auto delta = |
| 511 TimeTicks::Now() - WebContentsData::LastAudioChangeTime(contents); | 529 TimeTicks::Now() - GetWebContentsData(contents)->LastAudioChangeTime(); |
| 512 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); | 530 return delta < TimeDelta::FromSeconds(kAudioProtectionTimeSeconds); |
| 513 } | 531 } |
| 514 | 532 |
| 533 TabManager::WebContentsData* TabManager::GetWebContentsData( | |
| 534 content::WebContents* contents) const { | |
| 535 WebContentsData::CreateForWebContents(contents); | |
| 536 return WebContentsData::FromWebContents(contents); | |
| 537 } | |
| 538 | |
| 515 // static | 539 // static |
| 516 bool TabManager::CompareTabStats(TabStats first, TabStats second) { | 540 bool TabManager::CompareTabStats(TabStats first, TabStats second) { |
| 517 // Being currently selected is most important to protect. | 541 // Being currently selected is most important to protect. |
| 518 if (first.is_selected != second.is_selected) | 542 if (first.is_selected != second.is_selected) |
| 519 return first.is_selected; | 543 return first.is_selected; |
| 520 | 544 |
| 521 // Protect tabs with pending form entries. | 545 // Protect tabs with pending form entries. |
| 522 if (first.has_form_entry != second.has_form_entry) | 546 if (first.has_form_entry != second.has_form_entry) |
| 523 return first.has_form_entry; | 547 return first.has_form_entry; |
| 524 | 548 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 546 // sudden_termination_allowed false, and that covers too many common pages | 570 // sudden_termination_allowed false, and that covers too many common pages |
| 547 // with ad networks and statistics scripts. Ideally check for beforeUnload | 571 // 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 | 572 // handlers, which are likely to present a dialog asking if the user wants to |
| 549 // discard state. crbug.com/123049. | 573 // discard state. crbug.com/123049. |
| 550 | 574 |
| 551 // Being more recently active is more important. | 575 // Being more recently active is more important. |
| 552 return first.last_active > second.last_active; | 576 return first.last_active > second.last_active; |
| 553 } | 577 } |
| 554 | 578 |
| 555 } // namespace memory | 579 } // namespace memory |
| OLD | NEW |