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 |