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 |