Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: chrome/browser/tabs/tab_strip_model.cc

Issue 2821011: Removes phantom tabs. (Closed)
Patch Set: Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/tabs/tab_strip_model.h" 5 #include "chrome/browser/tabs/tab_strip_model.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/stl_util-inl.h" 10 #include "base/stl_util-inl.h"
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 } 73 }
74 74
75 void TabStripModel::AddObserver(TabStripModelObserver* observer) { 75 void TabStripModel::AddObserver(TabStripModelObserver* observer) {
76 observers_.AddObserver(observer); 76 observers_.AddObserver(observer);
77 } 77 }
78 78
79 void TabStripModel::RemoveObserver(TabStripModelObserver* observer) { 79 void TabStripModel::RemoveObserver(TabStripModelObserver* observer) {
80 observers_.RemoveObserver(observer); 80 observers_.RemoveObserver(observer);
81 } 81 }
82 82
83 bool TabStripModel::HasNonPhantomTabs() const {
84 for (int i = 0; i < count(); i++) {
85 if (!IsPhantomTab(i))
86 return true;
87 }
88 return false;
89 }
90
91 void TabStripModel::SetInsertionPolicy(InsertionPolicy policy) { 83 void TabStripModel::SetInsertionPolicy(InsertionPolicy policy) {
92 order_controller_->set_insertion_policy(policy); 84 order_controller_->set_insertion_policy(policy);
93 } 85 }
94 86
95 TabStripModel::InsertionPolicy TabStripModel::insertion_policy() const { 87 TabStripModel::InsertionPolicy TabStripModel::insertion_policy() const {
96 return order_controller_->insertion_policy(); 88 return order_controller_->insertion_policy();
97 } 89 }
98 90
99 bool TabStripModel::HasObserver(TabStripModelObserver* observer) { 91 bool TabStripModel::HasObserver(TabStripModelObserver* observer) {
100 return observers_.HasObserver(observer); 92 return observers_.HasObserver(observer);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 InternalCloseTabs(closing_tabs, CLOSE_NONE); 158 InternalCloseTabs(closing_tabs, CLOSE_NONE);
167 } 159 }
168 160
169 TabContents* TabStripModel::DetachTabContentsAt(int index) { 161 TabContents* TabStripModel::DetachTabContentsAt(int index) {
170 if (contents_data_.empty()) 162 if (contents_data_.empty())
171 return NULL; 163 return NULL;
172 164
173 DCHECK(ContainsIndex(index)); 165 DCHECK(ContainsIndex(index));
174 TabContents* removed_contents = GetContentsAt(index); 166 TabContents* removed_contents = GetContentsAt(index);
175 int next_selected_index = 167 int next_selected_index =
176 order_controller_->DetermineNewSelectedIndex(index, true); 168 order_controller_->DetermineNewSelectedIndex(index);
177 delete contents_data_.at(index); 169 delete contents_data_.at(index);
178 contents_data_.erase(contents_data_.begin() + index); 170 contents_data_.erase(contents_data_.begin() + index);
179 next_selected_index = IndexOfNextNonPhantomTab(next_selected_index, -1); 171 next_selected_index = IndexOfNextTab(next_selected_index);
180 if (!HasNonPhantomTabs()) 172 if (empty())
181 closing_all_ = true; 173 closing_all_ = true;
182 TabStripModelObservers::Iterator iter(observers_); 174 TabStripModelObservers::Iterator iter(observers_);
183 while (TabStripModelObserver* obs = iter.GetNext()) { 175 while (TabStripModelObserver* obs = iter.GetNext()) {
184 obs->TabDetachedAt(removed_contents, index); 176 obs->TabDetachedAt(removed_contents, index);
185 if (!HasNonPhantomTabs()) 177 if (empty())
186 obs->TabStripEmpty(); 178 obs->TabStripEmpty();
187 } 179 }
188 if (HasNonPhantomTabs()) { 180 if (!empty()) {
189 if (index == selected_index_) { 181 if (index == selected_index_) {
190 ChangeSelectedContentsFrom(removed_contents, next_selected_index, false); 182 ChangeSelectedContentsFrom(removed_contents, next_selected_index, false);
191 } else if (index < selected_index_) { 183 } else if (index < selected_index_) {
192 // The selected tab didn't change, but its position shifted; update our 184 // The selected tab didn't change, but its position shifted; update our
193 // index to continue to point at it. 185 // index to continue to point at it.
194 --selected_index_; 186 --selected_index_;
195 } 187 }
196 } 188 }
197 return removed_contents; 189 return removed_contents;
198 } 190 }
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 return contents_data_.at(index)->opener; 281 return contents_data_.at(index)->opener;
290 } 282 }
291 283
292 int TabStripModel::GetIndexOfNextTabContentsOpenedBy( 284 int TabStripModel::GetIndexOfNextTabContentsOpenedBy(
293 const NavigationController* opener, int start_index, bool use_group) const { 285 const NavigationController* opener, int start_index, bool use_group) const {
294 DCHECK(opener); 286 DCHECK(opener);
295 DCHECK(ContainsIndex(start_index)); 287 DCHECK(ContainsIndex(start_index));
296 288
297 // Check tabs after start_index first. 289 // Check tabs after start_index first.
298 for (int i = start_index + 1; i < count(); ++i) { 290 for (int i = start_index + 1; i < count(); ++i) {
299 if (OpenerMatches(contents_data_[i], opener, use_group) && 291 if (OpenerMatches(contents_data_[i], opener, use_group))
300 !IsPhantomTab(i)) {
301 return i; 292 return i;
302 }
303 } 293 }
304 // Then check tabs before start_index, iterating backwards. 294 // Then check tabs before start_index, iterating backwards.
305 for (int i = start_index - 1; i >= 0; --i) { 295 for (int i = start_index - 1; i >= 0; --i) {
306 if (OpenerMatches(contents_data_[i], opener, use_group) && 296 if (OpenerMatches(contents_data_[i], opener, use_group))
307 !IsPhantomTab(i)) {
308 return i; 297 return i;
309 }
310 } 298 }
311 return kNoTab; 299 return kNoTab;
312 } 300 }
313 301
314 int TabStripModel::GetIndexOfFirstTabContentsOpenedBy( 302 int TabStripModel::GetIndexOfFirstTabContentsOpenedBy(
315 const NavigationController* opener, 303 const NavigationController* opener,
316 int start_index) const { 304 int start_index) const {
317 DCHECK(opener); 305 DCHECK(opener);
318 DCHECK(ContainsIndex(start_index)); 306 DCHECK(ContainsIndex(start_index));
319 307
320 for (int i = 0; i < start_index; ++i) { 308 for (int i = 0; i < start_index; ++i) {
321 if (contents_data_[i]->opener == opener && !IsPhantomTab(i)) 309 if (contents_data_[i]->opener == opener)
322 return i; 310 return i;
323 } 311 }
324 return kNoTab; 312 return kNoTab;
325 } 313 }
326 314
327 int TabStripModel::GetIndexOfLastTabContentsOpenedBy( 315 int TabStripModel::GetIndexOfLastTabContentsOpenedBy(
328 const NavigationController* opener, int start_index) const { 316 const NavigationController* opener, int start_index) const {
329 DCHECK(opener); 317 DCHECK(opener);
330 DCHECK(ContainsIndex(start_index)); 318 DCHECK(ContainsIndex(start_index));
331 319
332 TabContentsDataVector::const_iterator end = 320 TabContentsDataVector::const_iterator end =
333 contents_data_.begin() + start_index; 321 contents_data_.begin() + start_index;
334 TabContentsDataVector::const_iterator iter = contents_data_.end(); 322 TabContentsDataVector::const_iterator iter = contents_data_.end();
335 TabContentsDataVector::const_iterator next; 323 TabContentsDataVector::const_iterator next;
336 for (; iter != end; --iter) { 324 for (; iter != end; --iter) {
337 next = iter - 1; 325 next = iter - 1;
338 if (next == end) 326 if (next == end)
339 break; 327 break;
340 if ((*next)->opener == opener && 328 if ((*next)->opener == opener)
341 !IsPhantomTab(static_cast<int>(next - contents_data_.begin()))) {
342 return static_cast<int>(next - contents_data_.begin()); 329 return static_cast<int>(next - contents_data_.begin());
343 }
344 } 330 }
345 return kNoTab; 331 return kNoTab;
346 } 332 }
347 333
348 void TabStripModel::TabNavigating(TabContents* contents, 334 void TabStripModel::TabNavigating(TabContents* contents,
349 PageTransition::Type transition) { 335 PageTransition::Type transition) {
350 if (ShouldForgetOpenersForTransition(transition)) { 336 if (ShouldForgetOpenersForTransition(transition)) {
351 // Don't forget the openers if this tab is a New Tab page opened at the 337 // Don't forget the openers if this tab is a New Tab page opened at the
352 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one 338 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
353 // navigation of one of these transition types before resetting the 339 // navigation of one of these transition types before resetting the
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 bool TabStripModel::IsToolbarVisible(int index) const { 433 bool TabStripModel::IsToolbarVisible(int index) const {
448 Extension* extension_app = GetTabContentsAt(index)->extension_app(); 434 Extension* extension_app = GetTabContentsAt(index)->extension_app();
449 if (!extension_app) 435 if (!extension_app)
450 return true; 436 return true;
451 437
452 ExtensionsService* service = profile()->GetExtensionsService(); 438 ExtensionsService* service = profile()->GetExtensionsService();
453 ExtensionPrefs* prefs = service->extension_prefs(); 439 ExtensionPrefs* prefs = service->extension_prefs();
454 return prefs->AreAppTabToolbarsVisible(extension_app->id()); 440 return prefs->AreAppTabToolbarsVisible(extension_app->id());
455 } 441 }
456 442
457 bool TabStripModel::IsPhantomTab(int index) const {
458 return IsTabPinned(index) &&
459 GetTabContentsAt(index)->controller().needs_reload();
460 }
461
462 bool TabStripModel::IsTabBlocked(int index) const { 443 bool TabStripModel::IsTabBlocked(int index) const {
463 return contents_data_[index]->blocked; 444 return contents_data_[index]->blocked;
464 } 445 }
465 446
466 int TabStripModel::IndexOfFirstNonMiniTab() const { 447 int TabStripModel::IndexOfFirstNonMiniTab() const {
467 for (size_t i = 0; i < contents_data_.size(); ++i) { 448 for (size_t i = 0; i < contents_data_.size(); ++i) {
468 if (!contents_data_[i]->contents->is_app() && !contents_data_[i]->pinned) 449 if (!contents_data_[i]->contents->is_app() && !contents_data_[i]->pinned)
469 return static_cast<int>(i); 450 return static_cast<int>(i);
470 } 451 }
471 // No mini-tabs. 452 // No mini-tabs.
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_RestoreTab"), 668 UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_RestoreTab"),
688 profile_); 669 profile_);
689 delegate_->RestoreTab(); 670 delegate_->RestoreTab();
690 break; 671 break;
691 } 672 }
692 case CommandTogglePinned: { 673 case CommandTogglePinned: {
693 UserMetrics::RecordAction( 674 UserMetrics::RecordAction(
694 UserMetricsAction("TabContextMenu_TogglePinned"), 675 UserMetricsAction("TabContextMenu_TogglePinned"),
695 profile_); 676 profile_);
696 677
697 if (IsPhantomTab(context_index)) { 678 SelectTabContentsAt(context_index, true);
698 // The tab is a phantom tab, close it. 679 SetTabPinned(context_index, !IsTabPinned(context_index));
699 CloseTabContentsAt(context_index,
700 CLOSE_USER_GESTURE | CLOSE_CREATE_HISTORICAL_TAB);
701 } else {
702 SelectTabContentsAt(context_index, true);
703 SetTabPinned(context_index, !IsTabPinned(context_index));
704 }
705 break; 680 break;
706 } 681 }
707 case CommandToggleToolbar: { 682 case CommandToggleToolbar: {
708 UserMetrics::RecordAction( 683 UserMetrics::RecordAction(
709 UserMetricsAction("TabContextMenu_ToggleToolbar"), 684 UserMetricsAction("TabContextMenu_ToggleToolbar"),
710 profile_); 685 profile_);
711 686
712 SelectTabContentsAt(context_index, true); 687 SelectTabContentsAt(context_index, true);
713 688
714 Extension* extension_app = 689 Extension* extension_app =
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
790 const NotificationDetails& details) { 765 const NotificationDetails& details) {
791 switch (type.value) { 766 switch (type.value) {
792 case NotificationType::TAB_CONTENTS_DESTROYED: { 767 case NotificationType::TAB_CONTENTS_DESTROYED: {
793 // Sometimes, on qemu, it seems like a TabContents object can be destroyed 768 // Sometimes, on qemu, it seems like a TabContents object can be destroyed
794 // while we still have a reference to it. We need to break this reference 769 // while we still have a reference to it. We need to break this reference
795 // here so we don't crash later. 770 // here so we don't crash later.
796 int index = GetIndexOfTabContents(Source<TabContents>(source).ptr()); 771 int index = GetIndexOfTabContents(Source<TabContents>(source).ptr());
797 if (index != TabStripModel::kNoTab) { 772 if (index != TabStripModel::kNoTab) {
798 // Note that we only detach the contents here, not close it - it's 773 // Note that we only detach the contents here, not close it - it's
799 // already been closed. We just want to undo our bookkeeping. 774 // already been closed. We just want to undo our bookkeeping.
800 if (ShouldMakePhantomOnClose(index)) { 775 DetachTabContentsAt(index);
801 // We don't actually allow pinned tabs to close. Instead they become
802 // phantom.
803 MakePhantom(index);
804 } else {
805 DetachTabContentsAt(index);
806 }
807 } 776 }
808 break; 777 break;
809 } 778 }
810 779
811 case NotificationType::EXTENSION_UNLOADED: { 780 case NotificationType::EXTENSION_UNLOADED: {
812 Extension* extension = Details<Extension>(details).ptr(); 781 Extension* extension = Details<Extension>(details).ptr();
813 // Iterate backwards as we may remove items while iterating. 782 // Iterate backwards as we may remove items while iterating.
814 for (int i = count() - 1; i >= 0; i--) { 783 for (int i = count() - 1; i >= 0; i--) {
815 TabContents* contents = GetTabContentsAt(i); 784 TabContents* contents = GetTabContentsAt(i);
816 if (contents->extension_app() == extension) { 785 if (contents->extension_app() == extension) {
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
964 int index = GetIndexOfTabContents(contents); 933 int index = GetIndexOfTabContents(contents);
965 contents_data_.at(index)->opener = &opener->controller(); 934 contents_data_.at(index)->opener = &opener->controller();
966 } 935 }
967 936
968 void TabStripModel::SelectRelativeTab(bool next) { 937 void TabStripModel::SelectRelativeTab(bool next) {
969 // This may happen during automated testing or if a user somehow buffers 938 // This may happen during automated testing or if a user somehow buffers
970 // many key accelerators. 939 // many key accelerators.
971 if (contents_data_.empty()) 940 if (contents_data_.empty())
972 return; 941 return;
973 942
974 // Skip pinned-app-phantom tabs when iterating.
975 int index = selected_index_;
976 int delta = next ? 1 : -1; 943 int delta = next ? 1 : -1;
977 do { 944 int index = (selected_index_ + count() + delta) % count();
978 index = (index + count() + delta) % count();
979 } while (index != selected_index_ && IsPhantomTab(index));
980 SelectTabContentsAt(index, true); 945 SelectTabContentsAt(index, true);
981 } 946 }
982 947
983 int TabStripModel::IndexOfNextNonPhantomTab(int index, 948 int TabStripModel::IndexOfNextTab(int index) {
984 int ignore_index) { 949 return (index == kNoTab || empty()) ?
985 if (index == kNoTab) 950 index : std::min(count() - 1, std::max(0, index));
986 return kNoTab;
987
988 if (empty())
989 return index;
990
991 index = std::min(count() - 1, std::max(0, index));
992 int start = index;
993 do {
994 if (index != ignore_index && !IsPhantomTab(index))
995 return index;
996 index = (index + 1) % count();
997 } while (index != start);
998
999 // All phantom tabs.
1000 return start;
1001 } 951 }
1002 952
1003 bool TabStripModel::ShouldMakePhantomOnClose(int index) {
1004 if (IsTabPinned(index) && !IsPhantomTab(index) && !closing_all_ &&
1005 profile()) {
1006 if (!IsAppTab(index))
1007 return true; // Always make non-app tabs go phantom.
1008
1009 ExtensionsService* extension_service = profile()->GetExtensionsService();
1010 if (!extension_service)
1011 return false;
1012
1013 Extension* extension_app = GetTabContentsAt(index)->extension_app();
1014 DCHECK(extension_app);
1015
1016 // Only allow the tab to be made phantom if the extension still exists.
1017 return extension_service->GetExtensionById(extension_app->id(),
1018 false) != NULL;
1019 }
1020 return false;
1021 }
1022
1023 void TabStripModel::MakePhantom(int index) {
1024 TabContents* old_contents = GetContentsAt(index);
1025 TabContents* new_contents = old_contents->CloneAndMakePhantom();
1026
1027 contents_data_[index]->contents = new_contents;
1028
1029 // And notify observers.
1030 FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1031 TabReplacedAt(old_contents, new_contents, index));
1032
1033 if (selected_index_ == index && HasNonPhantomTabs()) {
1034 // Change the selection, otherwise we're going to force the phantom tab
1035 // to become selected.
1036 // NOTE: we must do this after the call to Replace otherwise browser's
1037 // TabSelectedAt will send out updates for the old TabContents which we've
1038 // already told observers has been closed (we sent out TabClosing at).
1039 int new_selected_index =
1040 order_controller_->DetermineNewSelectedIndex(index, false);
1041 new_selected_index = IndexOfNextNonPhantomTab(new_selected_index,
1042 index);
1043 SelectTabContentsAt(new_selected_index, true);
1044 }
1045
1046 if (!HasNonPhantomTabs())
1047 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, TabStripEmpty());
1048 }
1049
1050
1051 void TabStripModel::MoveTabContentsAtImpl(int index, int to_position, 953 void TabStripModel::MoveTabContentsAtImpl(int index, int to_position,
1052 bool select_after_move) { 954 bool select_after_move) {
1053 TabContentsData* moved_data = contents_data_.at(index); 955 TabContentsData* moved_data = contents_data_.at(index);
1054 contents_data_.erase(contents_data_.begin() + index); 956 contents_data_.erase(contents_data_.begin() + index);
1055 contents_data_.insert(contents_data_.begin() + to_position, moved_data); 957 contents_data_.insert(contents_data_.begin() + to_position, moved_data);
1056 958
1057 // if !select_after_move, keep the same tab selected as was selected before. 959 // if !select_after_move, keep the same tab selected as was selected before.
1058 if (select_after_move || index == selected_index_) { 960 if (select_after_move || index == selected_index_) {
1059 selected_index_ = to_position; 961 selected_index_ = to_position;
1060 } else if (index < selected_index_ && to_position >= selected_index_) { 962 } else if (index < selected_index_ && to_position >= selected_index_) {
1061 selected_index_--; 963 selected_index_--;
1062 } else if (index > selected_index_ && to_position <= selected_index_) { 964 } else if (index > selected_index_ && to_position <= selected_index_) {
1063 selected_index_++; 965 selected_index_++;
1064 } 966 }
1065 967
1066 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, 968 FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1067 TabMoved(moved_data->contents, index, to_position)); 969 TabMoved(moved_data->contents, index, to_position));
1068 } 970 }
1069 971
1070 // static 972 // static
1071 bool TabStripModel::OpenerMatches(const TabContentsData* data, 973 bool TabStripModel::OpenerMatches(const TabContentsData* data,
1072 const NavigationController* opener, 974 const NavigationController* opener,
1073 bool use_group) { 975 bool use_group) {
1074 return data->opener == opener || (use_group && data->group == opener); 976 return data->opener == opener || (use_group && data->group == opener);
1075 } 977 }
OLDNEW
« no previous file with comments | « chrome/browser/tabs/tab_strip_model.h ('k') | chrome/browser/tabs/tab_strip_model_order_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698