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 |