| 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/ui/tabs/tab_strip_model.h" | 5 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| 11 | 11 |
| 12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 13 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
| 14 #include "chrome/app/chrome_command_ids.h" | 14 #include "chrome/app/chrome_command_ids.h" |
| 15 #include "chrome/browser/browser_shutdown.h" | 15 #include "chrome/browser/browser_shutdown.h" |
| 16 #include "chrome/browser/defaults.h" | 16 #include "chrome/browser/defaults.h" |
| 17 #include "chrome/browser/extensions/tab_helper.h" | 17 #include "chrome/browser/extensions/tab_helper.h" |
| 18 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
| 19 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 19 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| 20 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" | 20 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h" |
| 21 #include "chrome/browser/ui/tabs/tab_discard_state.h" |
| 21 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" | 22 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" |
| 22 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h" | 23 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h" |
| 23 #include "chrome/browser/ui/tabs/tab_utils.h" | 24 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 24 #include "chrome/browser/ui/web_contents_sizer.h" | 25 #include "chrome/browser/ui/web_contents_sizer.h" |
| 25 #include "chrome/common/url_constants.h" | 26 #include "chrome/common/url_constants.h" |
| 26 #include "components/web_modal/web_contents_modal_dialog_manager.h" | 27 #include "components/web_modal/web_contents_modal_dialog_manager.h" |
| 27 #include "content/public/browser/render_process_host.h" | 28 #include "content/public/browser/render_process_host.h" |
| 28 #include "content/public/browser/user_metrics.h" | 29 #include "content/public/browser/user_metrics.h" |
| 29 #include "content/public/browser/web_contents.h" | 30 #include "content/public/browser/web_contents.h" |
| 30 #include "content/public/browser/web_contents_observer.h" | 31 #include "content/public/browser/web_contents_observer.h" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 41 UMA_TAB_DISCARDING_DISCARD_TAB_AUDIO, | 42 UMA_TAB_DISCARDING_DISCARD_TAB_AUDIO, |
| 42 UMA_TAB_DISCARDING_TAB_DISCARDING_MAX | 43 UMA_TAB_DISCARDING_TAB_DISCARDING_MAX |
| 43 }; | 44 }; |
| 44 | 45 |
| 45 // Records an UMA tab discarding event. | 46 // Records an UMA tab discarding event. |
| 46 void RecordUMATabDiscarding(UMATabDiscarding event) { | 47 void RecordUMATabDiscarding(UMATabDiscarding event) { |
| 47 UMA_HISTOGRAM_ENUMERATION("Tab.Discarding", event, | 48 UMA_HISTOGRAM_ENUMERATION("Tab.Discarding", event, |
| 48 UMA_TAB_DISCARDING_TAB_DISCARDING_MAX); | 49 UMA_TAB_DISCARDING_TAB_DISCARDING_MAX); |
| 49 } | 50 } |
| 50 | 51 |
| 52 // Records the number of discards that a tab has been through. |
| 53 void RecordUMADiscardCount(int discard_count) { |
| 54 UMA_HISTOGRAM_COUNTS("Tab.Discarding.DiscardCount", discard_count); |
| 55 } |
| 56 |
| 51 // Returns true if the specified transition is one of the types that cause the | 57 // Returns true if the specified transition is one of the types that cause the |
| 52 // opener relationships for the tab in which the transition occurred to be | 58 // opener relationships for the tab in which the transition occurred to be |
| 53 // forgotten. This is generally any navigation that isn't a link click (i.e. | 59 // forgotten. This is generally any navigation that isn't a link click (i.e. |
| 54 // any navigation that can be considered to be the start of a new task distinct | 60 // any navigation that can be considered to be the start of a new task distinct |
| 55 // from what had previously occurred in that tab). | 61 // from what had previously occurred in that tab). |
| 56 bool ShouldForgetOpenersForTransition(ui::PageTransition transition) { | 62 bool ShouldForgetOpenersForTransition(ui::PageTransition transition) { |
| 57 return transition == ui::PAGE_TRANSITION_TYPED || | 63 return transition == ui::PAGE_TRANSITION_TYPED || |
| 58 transition == ui::PAGE_TRANSITION_AUTO_BOOKMARK || | 64 transition == ui::PAGE_TRANSITION_AUTO_BOOKMARK || |
| 59 transition == ui::PAGE_TRANSITION_GENERATED || | 65 transition == ui::PAGE_TRANSITION_GENERATED || |
| 60 transition == ui::PAGE_TRANSITION_KEYWORD || | 66 transition == ui::PAGE_TRANSITION_KEYWORD || |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 WebContents* opener() const { return opener_; } | 168 WebContents* opener() const { return opener_; } |
| 163 void set_opener(WebContents* value) { opener_ = value; } | 169 void set_opener(WebContents* value) { opener_ = value; } |
| 164 | 170 |
| 165 // Alters the properties of the WebContents. | 171 // Alters the properties of the WebContents. |
| 166 bool reset_group_on_select() const { return reset_group_on_select_; } | 172 bool reset_group_on_select() const { return reset_group_on_select_; } |
| 167 void set_reset_group_on_select(bool value) { reset_group_on_select_ = value; } | 173 void set_reset_group_on_select(bool value) { reset_group_on_select_ = value; } |
| 168 bool pinned() const { return pinned_; } | 174 bool pinned() const { return pinned_; } |
| 169 void set_pinned(bool value) { pinned_ = value; } | 175 void set_pinned(bool value) { pinned_ = value; } |
| 170 bool blocked() const { return blocked_; } | 176 bool blocked() const { return blocked_; } |
| 171 void set_blocked(bool value) { blocked_ = value; } | 177 void set_blocked(bool value) { blocked_ = value; } |
| 172 bool discarded() const { return discarded_; } | |
| 173 void set_discarded(bool value) { discarded_ = value; } | |
| 174 | 178 |
| 175 private: | 179 private: |
| 176 // Make sure that if someone deletes this WebContents out from under us, it | 180 // Make sure that if someone deletes this WebContents out from under us, it |
| 177 // is properly removed from the tab strip. | 181 // is properly removed from the tab strip. |
| 178 void WebContentsDestroyed() override; | 182 void WebContentsDestroyed() override; |
| 179 | 183 |
| 180 // Marks the tab as no longer discarded if it has been reloaded from another | 184 // Marks the tab as no longer discarded if it has been reloaded from another |
| 181 // source (ie: context menu). | 185 // source (ie: context menu). |
| 182 void DidStartLoading() override; | 186 void DidStartLoading() override; |
| 183 | 187 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 211 // selection shifts to _any_ tab (including their opener), the group | 215 // selection shifts to _any_ tab (including their opener), the group |
| 212 // relationship is reset to avoid confusing close sequencing. | 216 // relationship is reset to avoid confusing close sequencing. |
| 213 bool reset_group_on_select_; | 217 bool reset_group_on_select_; |
| 214 | 218 |
| 215 // Is the tab pinned? | 219 // Is the tab pinned? |
| 216 bool pinned_; | 220 bool pinned_; |
| 217 | 221 |
| 218 // Is the tab interaction blocked by a modal dialog? | 222 // Is the tab interaction blocked by a modal dialog? |
| 219 bool blocked_; | 223 bool blocked_; |
| 220 | 224 |
| 221 // Has the tab data been discarded to save memory? | |
| 222 bool discarded_; | |
| 223 | |
| 224 DISALLOW_COPY_AND_ASSIGN(WebContentsData); | 225 DISALLOW_COPY_AND_ASSIGN(WebContentsData); |
| 225 }; | 226 }; |
| 226 | 227 |
| 227 TabStripModel::WebContentsData::WebContentsData(TabStripModel* tab_strip_model, | 228 TabStripModel::WebContentsData::WebContentsData(TabStripModel* tab_strip_model, |
| 228 WebContents* contents) | 229 WebContents* contents) |
| 229 : content::WebContentsObserver(contents), | 230 : content::WebContentsObserver(contents), |
| 230 contents_(contents), | 231 contents_(contents), |
| 231 tab_strip_model_(tab_strip_model), | 232 tab_strip_model_(tab_strip_model), |
| 232 group_(NULL), | 233 group_(NULL), |
| 233 opener_(NULL), | 234 opener_(NULL), |
| 234 reset_group_on_select_(false), | 235 reset_group_on_select_(false), |
| 235 pinned_(false), | 236 pinned_(false), |
| 236 blocked_(false), | 237 blocked_(false) {} |
| 237 discarded_(false) { | |
| 238 } | |
| 239 | 238 |
| 240 void TabStripModel::WebContentsData::SetWebContents(WebContents* contents) { | 239 void TabStripModel::WebContentsData::SetWebContents(WebContents* contents) { |
| 241 contents_ = contents; | 240 contents_ = contents; |
| 242 Observe(contents); | 241 Observe(contents); |
| 243 } | 242 } |
| 244 | 243 |
| 245 void TabStripModel::WebContentsData::WebContentsDestroyed() { | 244 void TabStripModel::WebContentsData::WebContentsDestroyed() { |
| 246 DCHECK_EQ(contents_, web_contents()); | 245 DCHECK_EQ(contents_, web_contents()); |
| 247 | 246 |
| 248 // Note that we only detach the contents here, not close it - it's | 247 // Note that we only detach the contents here, not close it - it's |
| 249 // already been closed. We just want to undo our bookkeeping. | 248 // already been closed. We just want to undo our bookkeeping. |
| 250 int index = tab_strip_model_->GetIndexOfWebContents(web_contents()); | 249 int index = tab_strip_model_->GetIndexOfWebContents(web_contents()); |
| 251 DCHECK_NE(TabStripModel::kNoTab, index); | 250 DCHECK_NE(TabStripModel::kNoTab, index); |
| 252 tab_strip_model_->DetachWebContentsAt(index); | 251 tab_strip_model_->DetachWebContentsAt(index); |
| 253 } | 252 } |
| 254 | 253 |
| 255 void TabStripModel::WebContentsData::DidStartLoading() { | 254 void TabStripModel::WebContentsData::DidStartLoading() { |
| 256 set_discarded(false); | 255 TabDiscardState::SetDiscardState(contents_, false); |
| 257 } | 256 } |
| 258 | 257 |
| 259 /////////////////////////////////////////////////////////////////////////////// | 258 /////////////////////////////////////////////////////////////////////////////// |
| 260 // TabStripModel, public: | 259 // TabStripModel, public: |
| 261 | 260 |
| 262 TabStripModel::TabStripModel(TabStripModelDelegate* delegate, Profile* profile) | 261 TabStripModel::TabStripModel(TabStripModelDelegate* delegate, Profile* profile) |
| 263 : delegate_(delegate), | 262 : delegate_(delegate), |
| 264 profile_(profile), | 263 profile_(profile), |
| 265 closing_all_(false), | 264 closing_all_(false), |
| 266 in_notify_(false), | 265 in_notify_(false), |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 WebContents* null_contents = | 393 WebContents* null_contents = |
| 395 WebContents::Create(WebContents::CreateParams(profile())); | 394 WebContents::Create(WebContents::CreateParams(profile())); |
| 396 WebContents* old_contents = GetWebContentsAtImpl(index); | 395 WebContents* old_contents = GetWebContentsAtImpl(index); |
| 397 bool is_playing_audio = old_contents->WasRecentlyAudible(); | 396 bool is_playing_audio = old_contents->WasRecentlyAudible(); |
| 398 // Copy over the state from the navigation controller so we preserve the | 397 // Copy over the state from the navigation controller so we preserve the |
| 399 // back/forward history and continue to display the correct title/favicon. | 398 // back/forward history and continue to display the correct title/favicon. |
| 400 null_contents->GetController().CopyStateFrom(old_contents->GetController()); | 399 null_contents->GetController().CopyStateFrom(old_contents->GetController()); |
| 401 | 400 |
| 402 // Make sure we persist the last active time property. | 401 // Make sure we persist the last active time property. |
| 403 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); | 402 null_contents->SetLastActiveTime(old_contents->GetLastActiveTime()); |
| 403 // Copy over the discard count. |
| 404 TabDiscardState::CopyState(old_contents, null_contents); |
| 404 | 405 |
| 405 // Replace the tab we're discarding with the null version. | 406 // Replace the tab we're discarding with the null version. |
| 406 ReplaceWebContentsAt(index, null_contents); | 407 ReplaceWebContentsAt(index, null_contents); |
| 407 // Mark the tab so it will reload when we click. | 408 // Mark the tab so it will reload when we click. |
| 408 if (!contents_data_[index]->discarded()) { | 409 if (!TabDiscardState::IsDiscarded(null_contents)) { |
| 409 contents_data_[index]->set_discarded(true); | 410 TabDiscardState::SetDiscardState(null_contents, true); |
| 411 TabDiscardState::IncrementDiscardCount(null_contents); |
| 410 RecordUMATabDiscarding(UMA_TAB_DISCARDING_DISCARD_TAB); | 412 RecordUMATabDiscarding(UMA_TAB_DISCARDING_DISCARD_TAB); |
| 413 RecordUMADiscardCount(TabDiscardState::DiscardCount(null_contents)); |
| 411 if (is_playing_audio) | 414 if (is_playing_audio) |
| 412 RecordUMATabDiscarding(UMA_TAB_DISCARDING_DISCARD_TAB_AUDIO); | 415 RecordUMATabDiscarding(UMA_TAB_DISCARDING_DISCARD_TAB_AUDIO); |
| 413 } | 416 } |
| 414 // Discard the old tab's renderer. | 417 // Discard the old tab's renderer. |
| 415 // TODO(jamescook): This breaks script connections with other tabs. | 418 // TODO(jamescook): This breaks script connections with other tabs. |
| 416 // We need to find a different approach that doesn't do that, perhaps based | 419 // We need to find a different approach that doesn't do that, perhaps based |
| 417 // on navigation to swappedout://. | 420 // on navigation to swappedout://. |
| 418 delete old_contents; | 421 delete old_contents; |
| 419 return null_contents; | 422 return null_contents; |
| 420 } | 423 } |
| (...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 725 | 728 |
| 726 bool TabStripModel::IsTabPinned(int index) const { | 729 bool TabStripModel::IsTabPinned(int index) const { |
| 727 DCHECK(ContainsIndex(index)); | 730 DCHECK(ContainsIndex(index)); |
| 728 return contents_data_[index]->pinned(); | 731 return contents_data_[index]->pinned(); |
| 729 } | 732 } |
| 730 | 733 |
| 731 bool TabStripModel::IsTabBlocked(int index) const { | 734 bool TabStripModel::IsTabBlocked(int index) const { |
| 732 return contents_data_[index]->blocked(); | 735 return contents_data_[index]->blocked(); |
| 733 } | 736 } |
| 734 | 737 |
| 735 bool TabStripModel::IsTabDiscarded(int index) const { | |
| 736 return contents_data_[index]->discarded(); | |
| 737 } | |
| 738 | |
| 739 int TabStripModel::IndexOfFirstNonPinnedTab() const { | 738 int TabStripModel::IndexOfFirstNonPinnedTab() const { |
| 740 for (size_t i = 0; i < contents_data_.size(); ++i) { | 739 for (size_t i = 0; i < contents_data_.size(); ++i) { |
| 741 if (!IsTabPinned(static_cast<int>(i))) | 740 if (!IsTabPinned(static_cast<int>(i))) |
| 742 return static_cast<int>(i); | 741 return static_cast<int>(i); |
| 743 } | 742 } |
| 744 // No pinned tabs. | 743 // No pinned tabs. |
| 745 return count(); | 744 return count(); |
| 746 } | 745 } |
| 747 | 746 |
| 748 int TabStripModel::ConstrainInsertionIndex(int index, bool pinned_tab) { | 747 int TabStripModel::ConstrainInsertionIndex(int index, bool pinned_tab) { |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1320 ? TabStripModelObserver::CHANGE_REASON_USER_GESTURE | 1319 ? TabStripModelObserver::CHANGE_REASON_USER_GESTURE |
| 1321 : TabStripModelObserver::CHANGE_REASON_NONE; | 1320 : TabStripModelObserver::CHANGE_REASON_NONE; |
| 1322 CHECK(!in_notify_); | 1321 CHECK(!in_notify_); |
| 1323 in_notify_ = true; | 1322 in_notify_ = true; |
| 1324 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 1323 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 1325 ActiveTabChanged(old_contents, | 1324 ActiveTabChanged(old_contents, |
| 1326 new_contents, | 1325 new_contents, |
| 1327 active_index(), | 1326 active_index(), |
| 1328 reason)); | 1327 reason)); |
| 1329 in_notify_ = false; | 1328 in_notify_ = false; |
| 1330 // Activating a discarded tab reloads it, so it is no longer discarded. | |
| 1331 if (contents_data_[active_index()]->discarded()) { | |
| 1332 contents_data_[active_index()]->set_discarded(false); | |
| 1333 RecordUMATabDiscarding(UMA_TAB_DISCARDING_SWITCH_TO_DISCARDED_TAB); | |
| 1334 } else { | |
| 1335 RecordUMATabDiscarding(UMA_TAB_DISCARDING_SWITCH_TO_LOADED_TAB); | |
| 1336 } | |
| 1337 } | 1329 } |
| 1338 } | 1330 } |
| 1339 | 1331 |
| 1340 void TabStripModel::NotifyIfActiveOrSelectionChanged( | 1332 void TabStripModel::NotifyIfActiveOrSelectionChanged( |
| 1341 WebContents* old_contents, | 1333 WebContents* old_contents, |
| 1342 NotifyTypes notify_types, | 1334 NotifyTypes notify_types, |
| 1343 const ui::ListSelectionModel& old_model) { | 1335 const ui::ListSelectionModel& old_model) { |
| 1344 NotifyIfActiveTabChanged(old_contents, notify_types); | 1336 NotifyIfActiveTabChanged(old_contents, notify_types); |
| 1345 | 1337 |
| 1346 if (!selection_model().Equals(old_model)) { | 1338 if (!selection_model().Equals(old_model)) { |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1437 | 1429 |
| 1438 void TabStripModel::FixOpenersAndGroupsReferencing(int index) { | 1430 void TabStripModel::FixOpenersAndGroupsReferencing(int index) { |
| 1439 WebContents* old_contents = GetWebContentsAtImpl(index); | 1431 WebContents* old_contents = GetWebContentsAtImpl(index); |
| 1440 for (WebContentsData* data : contents_data_) { | 1432 for (WebContentsData* data : contents_data_) { |
| 1441 if (data->group() == old_contents) | 1433 if (data->group() == old_contents) |
| 1442 data->set_group(contents_data_[index]->group()); | 1434 data->set_group(contents_data_[index]->group()); |
| 1443 if (data->opener() == old_contents) | 1435 if (data->opener() == old_contents) |
| 1444 data->set_opener(contents_data_[index]->opener()); | 1436 data->set_opener(contents_data_[index]->opener()); |
| 1445 } | 1437 } |
| 1446 } | 1438 } |
| OLD | NEW |