| OLD | NEW |
| 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" |
| 11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "build/build_config.h" | 12 #include "build/build_config.h" |
| 13 #include "chrome/browser/bookmarks/bookmark_model.h" | 13 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 14 #include "chrome/browser/browser_shutdown.h" | 14 #include "chrome/browser/browser_shutdown.h" |
| 15 #include "chrome/browser/defaults.h" | 15 #include "chrome/browser/defaults.h" |
| 16 #include "chrome/browser/extensions/extensions_service.h" | 16 #include "chrome/browser/extensions/extensions_service.h" |
| 17 #include "chrome/browser/metrics/user_metrics.h" | 17 #include "chrome/browser/metrics/user_metrics.h" |
| 18 #include "chrome/browser/profile.h" | 18 #include "chrome/browser/profile.h" |
| 19 #include "chrome/browser/renderer_host/render_process_host.h" | 19 #include "chrome/browser/renderer_host/render_process_host.h" |
| 20 #include "chrome/browser/renderer_host/render_view_host.h" |
| 20 #include "chrome/browser/sessions/tab_restore_service.h" | 21 #include "chrome/browser/sessions/tab_restore_service.h" |
| 21 #include "chrome/browser/tabs/tab_strip_model_order_controller.h" | 22 #include "chrome/browser/tabs/tab_strip_model_order_controller.h" |
| 22 #include "chrome/browser/tab_contents/navigation_controller.h" | 23 #include "chrome/browser/tab_contents/navigation_controller.h" |
| 23 #include "chrome/browser/tab_contents/tab_contents.h" | 24 #include "chrome/browser/tab_contents/tab_contents.h" |
| 24 #include "chrome/browser/tab_contents/tab_contents_delegate.h" | 25 #include "chrome/browser/tab_contents/tab_contents_delegate.h" |
| 25 #include "chrome/browser/tab_contents/tab_contents_view.h" | 26 #include "chrome/browser/tab_contents/tab_contents_view.h" |
| 26 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
| 27 #include "chrome/common/extensions/extension.h" | 28 #include "chrome/common/extensions/extension.h" |
| 28 #include "chrome/common/notification_service.h" | 29 #include "chrome/common/notification_service.h" |
| 29 #include "chrome/common/url_constants.h" | 30 #include "chrome/common/url_constants.h" |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 } | 168 } |
| 168 | 169 |
| 169 TabContents* TabStripModel::DetachTabContentsAt(int index) { | 170 TabContents* TabStripModel::DetachTabContentsAt(int index) { |
| 170 if (contents_data_.empty()) | 171 if (contents_data_.empty()) |
| 171 return NULL; | 172 return NULL; |
| 172 | 173 |
| 173 DCHECK(ContainsIndex(index)); | 174 DCHECK(ContainsIndex(index)); |
| 174 TabContents* removed_contents = GetContentsAt(index); | 175 TabContents* removed_contents = GetContentsAt(index); |
| 175 int next_selected_index = | 176 int next_selected_index = |
| 176 order_controller_->DetermineNewSelectedIndex(index, true); | 177 order_controller_->DetermineNewSelectedIndex(index, true); |
| 177 delete contents_data_.at(index); | 178 delete contents_data_[index]; |
| 178 contents_data_.erase(contents_data_.begin() + index); | 179 contents_data_.erase(contents_data_.begin() + index); |
| 179 next_selected_index = IndexOfNextNonPhantomTab(next_selected_index, -1); | 180 next_selected_index = IndexOfNextNonPhantomTab(next_selected_index, -1); |
| 180 if (!HasNonPhantomTabs()) | 181 if (!HasNonPhantomTabs()) |
| 181 closing_all_ = true; | 182 closing_all_ = true; |
| 182 TabStripModelObservers::Iterator iter(observers_); | 183 TabStripModelObservers::Iterator iter(observers_); |
| 183 while (TabStripModelObserver* obs = iter.GetNext()) { | 184 while (TabStripModelObserver* obs = iter.GetNext()) { |
| 184 obs->TabDetachedAt(removed_contents, index); | 185 obs->TabDetachedAt(removed_contents, index); |
| 185 if (!HasNonPhantomTabs()) | 186 if (!HasNonPhantomTabs()) |
| 186 obs->TabStripEmpty(); | 187 obs->TabStripEmpty(); |
| 187 } | 188 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 TabContentsDataVector::const_iterator iter = contents_data_.begin(); | 280 TabContentsDataVector::const_iterator iter = contents_data_.begin(); |
| 280 for (; iter != contents_data_.end(); ++iter) { | 281 for (; iter != contents_data_.end(); ++iter) { |
| 281 if ((*iter)->contents->is_loading()) | 282 if ((*iter)->contents->is_loading()) |
| 282 return true; | 283 return true; |
| 283 } | 284 } |
| 284 return false; | 285 return false; |
| 285 } | 286 } |
| 286 | 287 |
| 287 NavigationController* TabStripModel::GetOpenerOfTabContentsAt(int index) { | 288 NavigationController* TabStripModel::GetOpenerOfTabContentsAt(int index) { |
| 288 DCHECK(ContainsIndex(index)); | 289 DCHECK(ContainsIndex(index)); |
| 289 return contents_data_.at(index)->opener; | 290 return contents_data_[index]->opener; |
| 290 } | 291 } |
| 291 | 292 |
| 292 int TabStripModel::GetIndexOfNextTabContentsOpenedBy( | 293 int TabStripModel::GetIndexOfNextTabContentsOpenedBy( |
| 293 const NavigationController* opener, int start_index, bool use_group) const { | 294 const NavigationController* opener, int start_index, bool use_group) const { |
| 294 DCHECK(opener); | 295 DCHECK(opener); |
| 295 DCHECK(ContainsIndex(start_index)); | 296 DCHECK(ContainsIndex(start_index)); |
| 296 | 297 |
| 297 // Check tabs after start_index first. | 298 // Check tabs after start_index first. |
| 298 for (int i = start_index + 1; i < count(); ++i) { | 299 for (int i = start_index + 1; i < count(); ++i) { |
| 299 if (OpenerMatches(contents_data_[i], opener, use_group) && | 300 if (OpenerMatches(contents_data_[i], opener, use_group) && |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 // Forget all opener memories so we don't do anything weird with tab | 372 // Forget all opener memories so we don't do anything weird with tab |
| 372 // re-selection ordering. | 373 // re-selection ordering. |
| 373 TabContentsDataVector::const_iterator iter = contents_data_.begin(); | 374 TabContentsDataVector::const_iterator iter = contents_data_.begin(); |
| 374 for (; iter != contents_data_.end(); ++iter) | 375 for (; iter != contents_data_.end(); ++iter) |
| 375 (*iter)->ForgetOpener(); | 376 (*iter)->ForgetOpener(); |
| 376 } | 377 } |
| 377 | 378 |
| 378 void TabStripModel::ForgetGroup(TabContents* contents) { | 379 void TabStripModel::ForgetGroup(TabContents* contents) { |
| 379 int index = GetIndexOfTabContents(contents); | 380 int index = GetIndexOfTabContents(contents); |
| 380 DCHECK(ContainsIndex(index)); | 381 DCHECK(ContainsIndex(index)); |
| 381 contents_data_.at(index)->SetGroup(NULL); | 382 contents_data_[index]->SetGroup(NULL); |
| 382 contents_data_.at(index)->ForgetOpener(); | 383 contents_data_[index]->ForgetOpener(); |
| 383 } | 384 } |
| 384 | 385 |
| 385 bool TabStripModel::ShouldResetGroupOnSelect(TabContents* contents) const { | 386 bool TabStripModel::ShouldResetGroupOnSelect(TabContents* contents) const { |
| 386 int index = GetIndexOfTabContents(contents); | 387 int index = GetIndexOfTabContents(contents); |
| 387 DCHECK(ContainsIndex(index)); | 388 DCHECK(ContainsIndex(index)); |
| 388 return contents_data_.at(index)->reset_group_on_select; | 389 return contents_data_[index]->reset_group_on_select; |
| 389 } | 390 } |
| 390 | 391 |
| 391 void TabStripModel::SetTabBlocked(int index, bool blocked) { | 392 void TabStripModel::SetTabBlocked(int index, bool blocked) { |
| 392 DCHECK(ContainsIndex(index)); | 393 DCHECK(ContainsIndex(index)); |
| 393 if (contents_data_[index]->blocked == blocked) | 394 if (contents_data_[index]->blocked == blocked) |
| 394 return; | 395 return; |
| 395 contents_data_[index]->blocked = blocked; | 396 contents_data_[index]->blocked = blocked; |
| 396 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 397 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 397 TabBlockedStateChanged(contents_data_[index]->contents, | 398 TabBlockedStateChanged(contents_data_[index]->contents, |
| 398 index)); | 399 index)); |
| 399 } | 400 } |
| 400 | 401 |
| 401 void TabStripModel::SetTabPinned(int index, bool pinned) { | 402 void TabStripModel::SetTabPinned(int index, bool pinned) { |
| 402 DCHECK(ContainsIndex(index)); | 403 DCHECK(ContainsIndex(index)); |
| 403 if (contents_data_[index]->pinned == pinned) | 404 if (contents_data_[index]->pinned == pinned) |
| 404 return; | 405 return; |
| 405 | 406 |
| 407 // Reroute toplevel requests to the browser if the tab is pinned. This allows |
| 408 // us to rewrite the WindowOpenDisposition in certain cases. |
| 409 TabContents* contents = contents_data_[index]->contents; |
| 410 contents->GetMutableRendererPrefs()->browser_handles_top_level_requests = |
| 411 pinned; |
| 412 if (contents->render_view_host()) |
| 413 contents->render_view_host()->SyncRendererPrefs(); |
| 414 |
| 406 if (IsAppTab(index)) { | 415 if (IsAppTab(index)) { |
| 407 // Changing the pinned state of an app tab doesn't effect it's mini-tab | 416 // Changing the pinned state of an app tab doesn't effect it's mini-tab |
| 408 // status. | 417 // status. |
| 409 contents_data_[index]->pinned = pinned; | 418 contents_data_[index]->pinned = pinned; |
| 410 } else { | 419 } else { |
| 411 // The tab is not an app tab, it's position may have to change as the | 420 // The tab is not an app tab, it's position may have to change as the |
| 412 // mini-tab state is changing. | 421 // mini-tab state is changing. |
| 413 int non_mini_tab_index = IndexOfFirstNonMiniTab(); | 422 int non_mini_tab_index = IndexOfFirstNonMiniTab(); |
| 414 contents_data_[index]->pinned = pinned; | 423 contents_data_[index]->pinned = pinned; |
| 415 if (pinned && index != non_mini_tab_index) { | 424 if (pinned && index != non_mini_tab_index) { |
| 416 MoveTabContentsAtImpl(index, non_mini_tab_index, false); | 425 MoveTabContentsAtImpl(index, non_mini_tab_index, false); |
| 417 return; // Don't send TabPinnedStateChanged notification. | 426 return; // Don't send TabPinnedStateChanged notification. |
| 418 } else if (!pinned && index + 1 != non_mini_tab_index) { | 427 } else if (!pinned && index + 1 != non_mini_tab_index) { |
| 419 MoveTabContentsAtImpl(index, non_mini_tab_index - 1, false); | 428 MoveTabContentsAtImpl(index, non_mini_tab_index - 1, false); |
| 420 return; // Don't send TabPinnedStateChanged notification. | 429 return; // Don't send TabPinnedStateChanged notification. |
| 421 } | 430 } |
| 422 | 431 |
| 423 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 432 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 424 TabMiniStateChanged(contents_data_[index]->contents, | 433 TabMiniStateChanged(contents, index)); |
| 425 index)); | |
| 426 } | 434 } |
| 427 | 435 |
| 428 // else: the tab was at the boundary and it's position doesn't need to | 436 // else: the tab was at the boundary and it's position doesn't need to |
| 429 // change. | 437 // change. |
| 430 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 438 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 431 TabPinnedStateChanged(contents_data_[index]->contents, | 439 TabPinnedStateChanged(contents, index)); |
| 432 index)); | |
| 433 } | 440 } |
| 434 | 441 |
| 435 bool TabStripModel::IsTabPinned(int index) const { | 442 bool TabStripModel::IsTabPinned(int index) const { |
| 436 return contents_data_[index]->pinned; | 443 return contents_data_[index]->pinned; |
| 437 } | 444 } |
| 438 | 445 |
| 439 bool TabStripModel::IsMiniTab(int index) const { | 446 bool TabStripModel::IsMiniTab(int index) const { |
| 440 return IsTabPinned(index) || IsAppTab(index); | 447 return IsTabPinned(index) || IsAppTab(index); |
| 441 } | 448 } |
| 442 | 449 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 // creates a New Tab (e.g. Ctrl+T, or clicks the New Tab button), or types | 518 // creates a New Tab (e.g. Ctrl+T, or clicks the New Tab button), or types |
| 512 // in the address bar and presses Alt+Enter. This allows for opening a new | 519 // in the address bar and presses Alt+Enter. This allows for opening a new |
| 513 // Tab to quickly look up something. When this Tab is closed, the old one | 520 // Tab to quickly look up something. When this Tab is closed, the old one |
| 514 // is re-selected, not the next-adjacent. | 521 // is re-selected, not the next-adjacent. |
| 515 inherit_group = true; | 522 inherit_group = true; |
| 516 } | 523 } |
| 517 InsertTabContentsAt(index, contents, foreground, inherit_group); | 524 InsertTabContentsAt(index, contents, foreground, inherit_group); |
| 518 // Reset the index, just in case insert ended up moving it on us. | 525 // Reset the index, just in case insert ended up moving it on us. |
| 519 index = GetIndexOfTabContents(contents); | 526 index = GetIndexOfTabContents(contents); |
| 520 if (inherit_group && transition == PageTransition::TYPED) | 527 if (inherit_group && transition == PageTransition::TYPED) |
| 521 contents_data_.at(index)->reset_group_on_select = true; | 528 contents_data_[index]->reset_group_on_select = true; |
| 522 | 529 |
| 523 // Ensure that the new TabContentsView begins at the same size as the | 530 // Ensure that the new TabContentsView begins at the same size as the |
| 524 // previous TabContentsView if it existed. Otherwise, the initial WebKit | 531 // previous TabContentsView if it existed. Otherwise, the initial WebKit |
| 525 // layout will be performed based on a width of 0 pixels, causing a | 532 // layout will be performed based on a width of 0 pixels, causing a |
| 526 // very long, narrow, inaccurate layout. Because some scripts on pages (as | 533 // very long, narrow, inaccurate layout. Because some scripts on pages (as |
| 527 // well as WebKit's anchor link location calculation) are run on the | 534 // well as WebKit's anchor link location calculation) are run on the |
| 528 // initial layout and not recalculated later, we need to ensure the first | 535 // initial layout and not recalculated later, we need to ensure the first |
| 529 // layout is performed with sane view dimensions even when we're opening a | 536 // layout is performed with sane view dimensions even when we're opening a |
| 530 // new background tab. | 537 // new background tab. |
| 531 if (TabContents* old_contents = GetSelectedTabContents()) { | 538 if (TabContents* old_contents = GetSelectedTabContents()) { |
| (...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 930 delegate_->CreateHistoricalTab(contents); | 937 delegate_->CreateHistoricalTab(contents); |
| 931 | 938 |
| 932 // Deleting the TabContents will call back to us via NotificationObserver | 939 // Deleting the TabContents will call back to us via NotificationObserver |
| 933 // and detach it. | 940 // and detach it. |
| 934 delete contents; | 941 delete contents; |
| 935 } | 942 } |
| 936 | 943 |
| 937 TabContents* TabStripModel::GetContentsAt(int index) const { | 944 TabContents* TabStripModel::GetContentsAt(int index) const { |
| 938 CHECK(ContainsIndex(index)) << | 945 CHECK(ContainsIndex(index)) << |
| 939 "Failed to find: " << index << " in: " << count() << " entries."; | 946 "Failed to find: " << index << " in: " << count() << " entries."; |
| 940 return contents_data_.at(index)->contents; | 947 return contents_data_[index]->contents; |
| 941 } | 948 } |
| 942 | 949 |
| 943 void TabStripModel::ChangeSelectedContentsFrom( | 950 void TabStripModel::ChangeSelectedContentsFrom( |
| 944 TabContents* old_contents, int to_index, bool user_gesture) { | 951 TabContents* old_contents, int to_index, bool user_gesture) { |
| 945 DCHECK(ContainsIndex(to_index)); | 952 DCHECK(ContainsIndex(to_index)); |
| 946 TabContents* new_contents = GetContentsAt(to_index); | 953 TabContents* new_contents = GetContentsAt(to_index); |
| 947 if (old_contents == new_contents) | 954 if (old_contents == new_contents) |
| 948 return; | 955 return; |
| 949 | 956 |
| 950 TabContents* last_selected_contents = old_contents; | 957 TabContents* last_selected_contents = old_contents; |
| 951 if (last_selected_contents) { | 958 if (last_selected_contents) { |
| 952 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 959 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 953 TabDeselectedAt(last_selected_contents, selected_index_)); | 960 TabDeselectedAt(last_selected_contents, selected_index_)); |
| 954 } | 961 } |
| 955 | 962 |
| 956 selected_index_ = to_index; | 963 selected_index_ = to_index; |
| 957 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 964 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 958 TabSelectedAt(last_selected_contents, new_contents, selected_index_, | 965 TabSelectedAt(last_selected_contents, new_contents, selected_index_, |
| 959 user_gesture)); | 966 user_gesture)); |
| 960 } | 967 } |
| 961 | 968 |
| 962 void TabStripModel::SetOpenerForContents(TabContents* contents, | 969 void TabStripModel::SetOpenerForContents(TabContents* contents, |
| 963 TabContents* opener) { | 970 TabContents* opener) { |
| 964 int index = GetIndexOfTabContents(contents); | 971 int index = GetIndexOfTabContents(contents); |
| 965 contents_data_.at(index)->opener = &opener->controller(); | 972 contents_data_[index]->opener = &opener->controller(); |
| 966 } | 973 } |
| 967 | 974 |
| 968 void TabStripModel::SelectRelativeTab(bool next) { | 975 void TabStripModel::SelectRelativeTab(bool next) { |
| 969 // This may happen during automated testing or if a user somehow buffers | 976 // This may happen during automated testing or if a user somehow buffers |
| 970 // many key accelerators. | 977 // many key accelerators. |
| 971 if (contents_data_.empty()) | 978 if (contents_data_.empty()) |
| 972 return; | 979 return; |
| 973 | 980 |
| 974 // Skip pinned-app-phantom tabs when iterating. | 981 // Skip pinned-app-phantom tabs when iterating. |
| 975 int index = selected_index_; | 982 int index = selected_index_; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1043 SelectTabContentsAt(new_selected_index, true); | 1050 SelectTabContentsAt(new_selected_index, true); |
| 1044 } | 1051 } |
| 1045 | 1052 |
| 1046 if (!HasNonPhantomTabs()) | 1053 if (!HasNonPhantomTabs()) |
| 1047 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, TabStripEmpty()); | 1054 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, TabStripEmpty()); |
| 1048 } | 1055 } |
| 1049 | 1056 |
| 1050 | 1057 |
| 1051 void TabStripModel::MoveTabContentsAtImpl(int index, int to_position, | 1058 void TabStripModel::MoveTabContentsAtImpl(int index, int to_position, |
| 1052 bool select_after_move) { | 1059 bool select_after_move) { |
| 1053 TabContentsData* moved_data = contents_data_.at(index); | 1060 TabContentsData* moved_data = contents_data_[index]; |
| 1054 contents_data_.erase(contents_data_.begin() + index); | 1061 contents_data_.erase(contents_data_.begin() + index); |
| 1055 contents_data_.insert(contents_data_.begin() + to_position, moved_data); | 1062 contents_data_.insert(contents_data_.begin() + to_position, moved_data); |
| 1056 | 1063 |
| 1057 // if !select_after_move, keep the same tab selected as was selected before. | 1064 // if !select_after_move, keep the same tab selected as was selected before. |
| 1058 if (select_after_move || index == selected_index_) { | 1065 if (select_after_move || index == selected_index_) { |
| 1059 selected_index_ = to_position; | 1066 selected_index_ = to_position; |
| 1060 } else if (index < selected_index_ && to_position >= selected_index_) { | 1067 } else if (index < selected_index_ && to_position >= selected_index_) { |
| 1061 selected_index_--; | 1068 selected_index_--; |
| 1062 } else if (index > selected_index_ && to_position <= selected_index_) { | 1069 } else if (index > selected_index_ && to_position <= selected_index_) { |
| 1063 selected_index_++; | 1070 selected_index_++; |
| 1064 } | 1071 } |
| 1065 | 1072 |
| 1066 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, | 1073 FOR_EACH_OBSERVER(TabStripModelObserver, observers_, |
| 1067 TabMoved(moved_data->contents, index, to_position)); | 1074 TabMoved(moved_data->contents, index, to_position)); |
| 1068 } | 1075 } |
| 1069 | 1076 |
| 1070 // static | 1077 // static |
| 1071 bool TabStripModel::OpenerMatches(const TabContentsData* data, | 1078 bool TabStripModel::OpenerMatches(const TabContentsData* data, |
| 1072 const NavigationController* opener, | 1079 const NavigationController* opener, |
| 1073 bool use_group) { | 1080 bool use_group) { |
| 1074 return data->opener == opener || (use_group && data->group == opener); | 1081 return data->opener == opener || (use_group && data->group == opener); |
| 1075 } | 1082 } |
| OLD | NEW |