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 |