Home | History | Annotate | Download | only in tabs
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/tabs/tab_strip_model.h"
      6 
      7 #include <algorithm>
      8 #include <map>
      9 
     10 #include "base/command_line.h"
     11 #include "base/stl_util-inl.h"
     12 #include "base/string_util.h"
     13 #include "build/build_config.h"
     14 #include "chrome/app/chrome_command_ids.h"
     15 #include "chrome/browser/bookmarks/bookmark_model.h"
     16 #include "chrome/browser/browser_shutdown.h"
     17 #include "chrome/browser/defaults.h"
     18 #include "chrome/browser/extensions/extension_service.h"
     19 #include "chrome/browser/extensions/extension_tab_helper.h"
     20 #include "chrome/browser/metrics/user_metrics.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/sessions/tab_restore_service.h"
     23 #include "chrome/browser/tabs/tab_strip_model_delegate.h"
     24 #include "chrome/browser/tabs/tab_strip_model_order_controller.h"
     25 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     26 #include "chrome/common/extensions/extension.h"
     27 #include "chrome/common/url_constants.h"
     28 #include "content/browser/renderer_host/render_process_host.h"
     29 #include "content/browser/tab_contents/navigation_controller.h"
     30 #include "content/browser/tab_contents/tab_contents.h"
     31 #include "content/browser/tab_contents/tab_contents_delegate.h"
     32 #include "content/browser/tab_contents/tab_contents_view.h"
     33 #include "content/common/notification_service.h"
     34 
     35 namespace {
     36 
     37 // Returns true if the specified transition is one of the types that cause the
     38 // opener relationships for the tab in which the transition occured to be
     39 // forgotten. This is generally any navigation that isn't a link click (i.e.
     40 // any navigation that can be considered to be the start of a new task distinct
     41 // from what had previously occurred in that tab).
     42 bool ShouldForgetOpenersForTransition(PageTransition::Type transition) {
     43   return transition == PageTransition::TYPED ||
     44       transition == PageTransition::AUTO_BOOKMARK ||
     45       transition == PageTransition::GENERATED ||
     46       transition == PageTransition::KEYWORD ||
     47       transition == PageTransition::START_PAGE;
     48 }
     49 
     50 }  // namespace
     51 
     52 ///////////////////////////////////////////////////////////////////////////////
     53 // TabStripModelDelegate, public:
     54 
     55 bool TabStripModelDelegate::CanCloseTab() const {
     56   return true;
     57 }
     58 
     59 ///////////////////////////////////////////////////////////////////////////////
     60 // TabStripModel, public:
     61 
     62 TabStripModel::TabStripModel(TabStripModelDelegate* delegate, Profile* profile)
     63     : delegate_(delegate),
     64       profile_(profile),
     65       closing_all_(false),
     66       order_controller_(NULL) {
     67   DCHECK(delegate_);
     68   registrar_.Add(this,
     69                  NotificationType::TAB_CONTENTS_DESTROYED,
     70                  NotificationService::AllSources());
     71   registrar_.Add(this,
     72                  NotificationType::EXTENSION_UNLOADED,
     73                  Source<Profile>(profile_));
     74   order_controller_ = new TabStripModelOrderController(this);
     75 }
     76 
     77 TabStripModel::~TabStripModel() {
     78   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
     79                     TabStripModelDeleted());
     80   STLDeleteContainerPointers(contents_data_.begin(), contents_data_.end());
     81   delete order_controller_;
     82 }
     83 
     84 void TabStripModel::AddObserver(TabStripModelObserver* observer) {
     85   observers_.AddObserver(observer);
     86 }
     87 
     88 void TabStripModel::RemoveObserver(TabStripModelObserver* observer) {
     89   observers_.RemoveObserver(observer);
     90 }
     91 
     92 void TabStripModel::SetInsertionPolicy(InsertionPolicy policy) {
     93   order_controller_->set_insertion_policy(policy);
     94 }
     95 
     96 TabStripModel::InsertionPolicy TabStripModel::insertion_policy() const {
     97   return order_controller_->insertion_policy();
     98 }
     99 
    100 bool TabStripModel::HasObserver(TabStripModelObserver* observer) {
    101   return observers_.HasObserver(observer);
    102 }
    103 
    104 bool TabStripModel::ContainsIndex(int index) const {
    105   return index >= 0 && index < count();
    106 }
    107 
    108 void TabStripModel::AppendTabContents(TabContentsWrapper* contents,
    109                                       bool foreground) {
    110   int index = order_controller_->DetermineInsertionIndexForAppending();
    111   InsertTabContentsAt(index, contents,
    112                       foreground ? (ADD_INHERIT_GROUP | ADD_ACTIVE) :
    113                                    ADD_NONE);
    114 }
    115 
    116 void TabStripModel::InsertTabContentsAt(int index,
    117                                         TabContentsWrapper* contents,
    118                                         int add_types) {
    119   bool active = add_types & ADD_ACTIVE;
    120   // Force app tabs to be pinned.
    121   bool pin =
    122       contents->extension_tab_helper()->is_app() || add_types & ADD_PINNED;
    123   index = ConstrainInsertionIndex(index, pin);
    124 
    125   // In tab dragging situations, if the last tab in the window was detached
    126   // then the user aborted the drag, we will have the |closing_all_| member
    127   // set (see DetachTabContentsAt) which will mess with our mojo here. We need
    128   // to clear this bit.
    129   closing_all_ = false;
    130 
    131   // Have to get the selected contents before we monkey with |contents_|
    132   // otherwise we run into problems when we try to change the selected contents
    133   // since the old contents and the new contents will be the same...
    134   TabContentsWrapper* selected_contents = GetSelectedTabContents();
    135   TabContentsData* data = new TabContentsData(contents);
    136   data->pinned = pin;
    137   if ((add_types & ADD_INHERIT_GROUP) && selected_contents) {
    138     if (active) {
    139       // Forget any existing relationships, we don't want to make things too
    140       // confusing by having multiple groups active at the same time.
    141       ForgetAllOpeners();
    142     }
    143     // Anything opened by a link we deem to have an opener.
    144     data->SetGroup(&selected_contents->controller());
    145   } else if ((add_types & ADD_INHERIT_OPENER) && selected_contents) {
    146     if (active) {
    147       // Forget any existing relationships, we don't want to make things too
    148       // confusing by having multiple groups active at the same time.
    149       ForgetAllOpeners();
    150     }
    151     data->opener = &selected_contents->controller();
    152   }
    153 
    154   contents_data_.insert(contents_data_.begin() + index, data);
    155 
    156   selection_model_.IncrementFrom(index);
    157 
    158   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    159                     TabInsertedAt(contents, index, active));
    160 
    161   if (active) {
    162     selection_model_.SetSelectedIndex(index);
    163     NotifyTabSelectedIfChanged(selected_contents, index, false);
    164   }
    165 }
    166 
    167 TabContentsWrapper* TabStripModel::ReplaceTabContentsAt(
    168     int index,
    169     TabContentsWrapper* new_contents) {
    170   DCHECK(ContainsIndex(index));
    171   TabContentsWrapper* old_contents = GetContentsAt(index);
    172 
    173   ForgetOpenersAndGroupsReferencing(&(old_contents->controller()));
    174 
    175   contents_data_[index]->contents = new_contents;
    176 
    177   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    178                     TabReplacedAt(this, old_contents, new_contents, index));
    179 
    180   // When the active tab contents is replaced send out selected notification
    181   // too. We do this as nearly all observers need to treat a replace of the
    182   // selected contents as selection changing.
    183   if (active_index() == index) {
    184     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    185                       TabSelectedAt(old_contents, new_contents, active_index(),
    186                                     false));
    187   }
    188   return old_contents;
    189 }
    190 
    191 void TabStripModel::ReplaceNavigationControllerAt(
    192     int index, TabContentsWrapper* contents) {
    193   // This appears to be OK with no flicker since no redraw event
    194   // occurs between the call to add an aditional tab and one to close
    195   // the previous tab.
    196   InsertTabContentsAt(index + 1, contents, ADD_ACTIVE | ADD_INHERIT_GROUP);
    197   std::vector<int> closing_tabs;
    198   closing_tabs.push_back(index);
    199   InternalCloseTabs(closing_tabs, CLOSE_NONE);
    200 }
    201 
    202 TabContentsWrapper* TabStripModel::DetachTabContentsAt(int index) {
    203   if (contents_data_.empty())
    204     return NULL;
    205 
    206   DCHECK(ContainsIndex(index));
    207 
    208   TabContentsWrapper* removed_contents = GetContentsAt(index);
    209   int next_selected_index = order_controller_->DetermineNewSelectedIndex(index);
    210   delete contents_data_.at(index);
    211   contents_data_.erase(contents_data_.begin() + index);
    212   ForgetOpenersAndGroupsReferencing(&(removed_contents->controller()));
    213   if (empty())
    214     closing_all_ = true;
    215   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    216       TabDetachedAt(removed_contents, index));
    217   if (empty()) {
    218     // TabDetachedAt() might unregister observers, so send |TabStripEmtpy()| in
    219     // a second pass.
    220     FOR_EACH_OBSERVER(TabStripModelObserver, observers_, TabStripEmpty());
    221   } else {
    222     int old_active = active_index();
    223     selection_model_.DecrementFrom(index);
    224     if (index == old_active) {
    225       if (!selection_model_.empty()) {
    226         // A selected tab was removed, but there is still something selected.
    227         // Move the active and anchor to the first selected index.
    228         selection_model_.set_active(selection_model_.selected_indices()[0]);
    229         selection_model_.set_anchor(selection_model_.active());
    230         NotifyTabSelectedIfChanged(removed_contents, active_index(), false);
    231       } else {
    232         // The active tab was removed and nothing is selected. Reset the
    233         // selection and send out notification.
    234         selection_model_.SetSelectedIndex(next_selected_index);
    235         NotifyTabSelectedIfChanged(removed_contents, next_selected_index,
    236                                    false);
    237       }
    238     }
    239   }
    240   return removed_contents;
    241 }
    242 
    243 void TabStripModel::ActivateTabAt(int index, bool user_gesture) {
    244   DCHECK(ContainsIndex(index));
    245   bool had_multi = selection_model_.selected_indices().size() > 1;
    246   TabContentsWrapper* old_contents =
    247       (active_index() == TabStripSelectionModel::kUnselectedIndex) ?
    248       NULL : GetSelectedTabContents();
    249   selection_model_.SetSelectedIndex(index);
    250   TabContentsWrapper* new_contents = GetContentsAt(index);
    251   if (old_contents != new_contents && old_contents) {
    252     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    253                       TabDeselected(old_contents));
    254   }
    255   if (old_contents != new_contents || had_multi) {
    256     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    257                       TabSelectedAt(old_contents, new_contents,
    258                                     active_index(), user_gesture));
    259   }
    260 }
    261 
    262 void TabStripModel::MoveTabContentsAt(int index,
    263                                       int to_position,
    264                                       bool select_after_move) {
    265   DCHECK(ContainsIndex(index));
    266   if (index == to_position)
    267     return;
    268 
    269   int first_non_mini_tab = IndexOfFirstNonMiniTab();
    270   if ((index < first_non_mini_tab && to_position >= first_non_mini_tab) ||
    271       (to_position < first_non_mini_tab && index >= first_non_mini_tab)) {
    272     // This would result in mini tabs mixed with non-mini tabs. We don't allow
    273     // that.
    274     return;
    275   }
    276 
    277   MoveTabContentsAtImpl(index, to_position, select_after_move);
    278 }
    279 
    280 void TabStripModel::MoveSelectedTabsTo(int index) {
    281   int total_mini_count = IndexOfFirstNonMiniTab();
    282   int selected_mini_count = 0;
    283   int selected_count =
    284       static_cast<int>(selection_model_.selected_indices().size());
    285   for (int i = 0; i < selected_count &&
    286            IsMiniTab(selection_model_.selected_indices()[i]); ++i) {
    287     selected_mini_count++;
    288   }
    289 
    290   // To maintain that all mini-tabs occur before non-mini-tabs we move them
    291   // first.
    292   if (selected_mini_count > 0) {
    293     MoveSelectedTabsToImpl(
    294         std::min(total_mini_count - selected_mini_count, index), 0u,
    295         selected_mini_count);
    296     if (index > total_mini_count - selected_mini_count) {
    297       // We're being told to drag mini-tabs to an invalid location. Adjust the
    298       // index such that non-mini-tabs end up at a location as though we could
    299       // move the mini-tabs to index. See description in header for more
    300       // details.
    301       index += selected_mini_count;
    302     }
    303   }
    304   if (selected_mini_count == selected_count)
    305     return;
    306 
    307   // Then move the non-pinned tabs.
    308   MoveSelectedTabsToImpl(std::max(index, total_mini_count),
    309                          selected_mini_count,
    310                          selected_count - selected_mini_count);
    311 }
    312 
    313 TabContentsWrapper* TabStripModel::GetSelectedTabContents() const {
    314   return GetTabContentsAt(active_index());
    315 }
    316 
    317 TabContentsWrapper* TabStripModel::GetTabContentsAt(int index) const {
    318   if (ContainsIndex(index))
    319     return GetContentsAt(index);
    320   return NULL;
    321 }
    322 
    323 int TabStripModel::GetIndexOfTabContents(
    324     const TabContentsWrapper* contents) const {
    325   int index = 0;
    326   TabContentsDataVector::const_iterator iter = contents_data_.begin();
    327   for (; iter != contents_data_.end(); ++iter, ++index) {
    328     if ((*iter)->contents == contents)
    329       return index;
    330   }
    331   return kNoTab;
    332 }
    333 
    334 int TabStripModel::GetWrapperIndex(const TabContents* contents) const {
    335   int index = 0;
    336   TabContentsDataVector::const_iterator iter = contents_data_.begin();
    337   for (; iter != contents_data_.end(); ++iter, ++index) {
    338     if ((*iter)->contents->tab_contents() == contents)
    339       return index;
    340   }
    341   return kNoTab;
    342 }
    343 
    344 int TabStripModel::GetIndexOfController(
    345     const NavigationController* controller) const {
    346   int index = 0;
    347   TabContentsDataVector::const_iterator iter = contents_data_.begin();
    348   for (; iter != contents_data_.end(); ++iter, ++index) {
    349     if (&(*iter)->contents->controller() == controller)
    350       return index;
    351   }
    352   return kNoTab;
    353 }
    354 
    355 void TabStripModel::UpdateTabContentsStateAt(int index,
    356     TabStripModelObserver::TabChangeType change_type) {
    357   DCHECK(ContainsIndex(index));
    358 
    359   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    360       TabChangedAt(GetContentsAt(index), index, change_type));
    361 }
    362 
    363 void TabStripModel::CloseAllTabs() {
    364   // Set state so that observers can adjust their behavior to suit this
    365   // specific condition when CloseTabContentsAt causes a flurry of
    366   // Close/Detach/Select notifications to be sent.
    367   closing_all_ = true;
    368   std::vector<int> closing_tabs;
    369   for (int i = count() - 1; i >= 0; --i)
    370     closing_tabs.push_back(i);
    371   InternalCloseTabs(closing_tabs, CLOSE_CREATE_HISTORICAL_TAB);
    372 }
    373 
    374 bool TabStripModel::CloseTabContentsAt(int index, uint32 close_types) {
    375   std::vector<int> closing_tabs;
    376   closing_tabs.push_back(index);
    377   return InternalCloseTabs(closing_tabs, close_types);
    378 }
    379 
    380 bool TabStripModel::TabsAreLoading() const {
    381   TabContentsDataVector::const_iterator iter = contents_data_.begin();
    382   for (; iter != contents_data_.end(); ++iter) {
    383     if ((*iter)->contents->tab_contents()->is_loading())
    384       return true;
    385   }
    386   return false;
    387 }
    388 
    389 NavigationController* TabStripModel::GetOpenerOfTabContentsAt(int index) {
    390   DCHECK(ContainsIndex(index));
    391   return contents_data_.at(index)->opener;
    392 }
    393 
    394 int TabStripModel::GetIndexOfNextTabContentsOpenedBy(
    395     const NavigationController* opener, int start_index, bool use_group) const {
    396   DCHECK(opener);
    397   DCHECK(ContainsIndex(start_index));
    398 
    399   // Check tabs after start_index first.
    400   for (int i = start_index + 1; i < count(); ++i) {
    401     if (OpenerMatches(contents_data_[i], opener, use_group))
    402       return i;
    403   }
    404   // Then check tabs before start_index, iterating backwards.
    405   for (int i = start_index - 1; i >= 0; --i) {
    406     if (OpenerMatches(contents_data_[i], opener, use_group))
    407       return i;
    408   }
    409   return kNoTab;
    410 }
    411 
    412 int TabStripModel::GetIndexOfFirstTabContentsOpenedBy(
    413     const NavigationController* opener,
    414     int start_index) const {
    415   DCHECK(opener);
    416   DCHECK(ContainsIndex(start_index));
    417 
    418   for (int i = 0; i < start_index; ++i) {
    419     if (contents_data_[i]->opener == opener)
    420       return i;
    421   }
    422   return kNoTab;
    423 }
    424 
    425 int TabStripModel::GetIndexOfLastTabContentsOpenedBy(
    426     const NavigationController* opener, int start_index) const {
    427   DCHECK(opener);
    428   DCHECK(ContainsIndex(start_index));
    429 
    430   TabContentsDataVector::const_iterator end =
    431       contents_data_.begin() + start_index;
    432   TabContentsDataVector::const_iterator iter = contents_data_.end();
    433   TabContentsDataVector::const_iterator next;
    434   for (; iter != end; --iter) {
    435     next = iter - 1;
    436     if (next == end)
    437       break;
    438     if ((*next)->opener == opener)
    439       return static_cast<int>(next - contents_data_.begin());
    440   }
    441   return kNoTab;
    442 }
    443 
    444 void TabStripModel::TabNavigating(TabContentsWrapper* contents,
    445                                   PageTransition::Type transition) {
    446   if (ShouldForgetOpenersForTransition(transition)) {
    447     // Don't forget the openers if this tab is a New Tab page opened at the
    448     // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
    449     // navigation of one of these transition types before resetting the
    450     // opener relationships (this allows for the use case of opening a new
    451     // tab to do a quick look-up of something while viewing a tab earlier in
    452     // the strip). We can make this heuristic more permissive if need be.
    453     if (!IsNewTabAtEndOfTabStrip(contents)) {
    454       // If the user navigates the current tab to another page in any way
    455       // other than by clicking a link, we want to pro-actively forget all
    456       // TabStrip opener relationships since we assume they're beginning a
    457       // different task by reusing the current tab.
    458       ForgetAllOpeners();
    459       // In this specific case we also want to reset the group relationship,
    460       // since it is now technically invalid.
    461       ForgetGroup(contents);
    462     }
    463   }
    464 }
    465 
    466 void TabStripModel::ForgetAllOpeners() {
    467   // Forget all opener memories so we don't do anything weird with tab
    468   // re-selection ordering.
    469   TabContentsDataVector::const_iterator iter = contents_data_.begin();
    470   for (; iter != contents_data_.end(); ++iter)
    471     (*iter)->ForgetOpener();
    472 }
    473 
    474 void TabStripModel::ForgetGroup(TabContentsWrapper* contents) {
    475   int index = GetIndexOfTabContents(contents);
    476   DCHECK(ContainsIndex(index));
    477   contents_data_.at(index)->SetGroup(NULL);
    478   contents_data_.at(index)->ForgetOpener();
    479 }
    480 
    481 bool TabStripModel::ShouldResetGroupOnSelect(
    482     TabContentsWrapper* contents) const {
    483   int index = GetIndexOfTabContents(contents);
    484   DCHECK(ContainsIndex(index));
    485   return contents_data_.at(index)->reset_group_on_select;
    486 }
    487 
    488 void TabStripModel::SetTabBlocked(int index, bool blocked) {
    489   DCHECK(ContainsIndex(index));
    490   if (contents_data_[index]->blocked == blocked)
    491     return;
    492   contents_data_[index]->blocked = blocked;
    493   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    494       TabBlockedStateChanged(contents_data_[index]->contents,
    495       index));
    496 }
    497 
    498 void TabStripModel::SetTabPinned(int index, bool pinned) {
    499   DCHECK(ContainsIndex(index));
    500   if (contents_data_[index]->pinned == pinned)
    501     return;
    502 
    503   if (IsAppTab(index)) {
    504     if (!pinned) {
    505       // App tabs should always be pinned.
    506       NOTREACHED();
    507       return;
    508     }
    509     // Changing the pinned state of an app tab doesn't effect it's mini-tab
    510     // status.
    511     contents_data_[index]->pinned = pinned;
    512   } else {
    513     // The tab is not an app tab, it's position may have to change as the
    514     // mini-tab state is changing.
    515     int non_mini_tab_index = IndexOfFirstNonMiniTab();
    516     contents_data_[index]->pinned = pinned;
    517     if (pinned && index != non_mini_tab_index) {
    518       MoveTabContentsAtImpl(index, non_mini_tab_index, false);
    519       index = non_mini_tab_index;
    520     } else if (!pinned && index + 1 != non_mini_tab_index) {
    521       MoveTabContentsAtImpl(index, non_mini_tab_index - 1, false);
    522       index = non_mini_tab_index - 1;
    523     }
    524 
    525     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    526                       TabMiniStateChanged(contents_data_[index]->contents,
    527                                           index));
    528   }
    529 
    530   // else: the tab was at the boundary and it's position doesn't need to
    531   // change.
    532   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
    533                     TabPinnedStateChanged(contents_data_[index]->contents,
    534                                           index));
    535 }
    536 
    537 bool TabStripModel::IsTabPinned(int index) const {
    538   DCHECK(ContainsIndex(index));
    539   return contents_data_[index]->pinned;
    540 }
    541 
    542 bool TabStripModel::IsMiniTab(int index) const {
    543   return IsTabPinned(index) || IsAppTab(index);
    544 }
    545 
    546 bool TabStripModel::IsAppTab(int index) const {
    547   TabContentsWrapper* contents = GetTabContentsAt(index);
    548   return contents && contents->extension_tab_helper()->is_app();
    549 }
    550 
    551 bool TabStripModel::IsTabBlocked(int index) const {
    552   return contents_data_[index]->blocked;
    553 }
    554 
    555 int TabStripModel::IndexOfFirstNonMiniTab() const {
    556   for (size_t i = 0; i < contents_data_.size(); ++i) {
    557     if (!IsMiniTab(static_cast<int>(i)))
    558       return static_cast<int>(i);
    559   }
    560   // No mini-tabs.
    561   return count();
    562 }
    563 
    564 int TabStripModel::ConstrainInsertionIndex(int index, bool mini_tab) {
    565   return mini_tab ? std::min(std::max(0, index), IndexOfFirstNonMiniTab()) :
    566       std::min(count(), std::max(index, IndexOfFirstNonMiniTab()));
    567 }
    568 
    569 void TabStripModel::ExtendSelectionTo(int index) {
    570   DCHECK(ContainsIndex(index));
    571   int old_active = active_index();
    572   selection_model_.SetSelectionFromAnchorTo(index);
    573   // This may not have resulted in a change, but we assume it did.
    574   NotifySelectionChanged(old_active);
    575 }
    576 
    577 void TabStripModel::ToggleSelectionAt(int index) {
    578   DCHECK(ContainsIndex(index));
    579   int old_active = active_index();
    580   if (selection_model_.IsSelected(index)) {
    581     if (selection_model_.size() == 1) {
    582       // One tab must be selected and this tab is currently selected so we can't
    583       // unselect it.
    584       return;
    585     }
    586     selection_model_.RemoveIndexFromSelection(index);
    587     selection_model_.set_anchor(index);
    588     if (selection_model_.active() == TabStripSelectionModel::kUnselectedIndex)
    589       selection_model_.set_active(selection_model_.selected_indices()[0]);
    590   } else {
    591     selection_model_.AddIndexToSelection(index);
    592     selection_model_.set_anchor(index);
    593     selection_model_.set_active(index);
    594   }
    595   NotifySelectionChanged(old_active);
    596 }
    597 
    598 void TabStripModel::AddSelectionFromAnchorTo(int index) {
    599   int old_active = active_index();
    600   selection_model_.AddSelectionFromAnchorTo(index);
    601   NotifySelectionChanged(old_active);
    602 }
    603 
    604 bool TabStripModel::IsTabSelected(int index) const {
    605   DCHECK(ContainsIndex(index));
    606   return selection_model_.IsSelected(index);
    607 }
    608 
    609 void TabStripModel::SetSelectionFromModel(
    610     const TabStripSelectionModel& source) {
    611   DCHECK_NE(TabStripSelectionModel::kUnselectedIndex, source.active());
    612   int old_active_index = active_index();
    613   selection_model_.Copy(source);
    614   // This may not have resulted in a change, but we assume it did.
    615   NotifySelectionChanged(old_active_index);
    616 }
    617 
    618 void TabStripModel::AddTabContents(TabContentsWrapper* contents,
    619                                    int index,
    620                                    PageTransition::Type transition,
    621                                    int add_types) {
    622   // If the newly-opened tab is part of the same task as the parent tab, we want
    623   // to inherit the parent's "group" attribute, so that if this tab is then
    624   // closed we'll jump back to the parent tab.
    625   bool inherit_group = (add_types & ADD_INHERIT_GROUP) == ADD_INHERIT_GROUP;
    626 
    627   if (transition == PageTransition::LINK &&
    628       (add_types & ADD_FORCE_INDEX) == 0) {
    629     // We assume tabs opened via link clicks are part of the same task as their
    630     // parent.  Note that when |force_index| is true (e.g. when the user
    631     // drag-and-drops a link to the tab strip), callers aren't really handling
    632     // link clicks, they just want to score the navigation like a link click in
    633     // the history backend, so we don't inherit the group in this case.
    634     index = order_controller_->DetermineInsertionIndex(
    635         contents, transition, add_types & ADD_ACTIVE);
    636     inherit_group = true;
    637   } else {
    638     // For all other types, respect what was passed to us, normalizing -1s and
    639     // values that are too large.
    640     if (index < 0 || index > count())
    641       index = order_controller_->DetermineInsertionIndexForAppending();
    642   }
    643 
    644   if (transition == PageTransition::TYPED && index == count()) {
    645     // Also, any tab opened at the end of the TabStrip with a "TYPED"
    646     // transition inherit group as well. This covers the cases where the user
    647     // creates a New Tab (e.g. Ctrl+T, or clicks the New Tab button), or types
    648     // in the address bar and presses Alt+Enter. This allows for opening a new
    649     // Tab to quickly look up something. When this Tab is closed, the old one
    650     // is re-selected, not the next-adjacent.
    651     inherit_group = true;
    652   }
    653   InsertTabContentsAt(
    654       index, contents,
    655       add_types | (inherit_group ? ADD_INHERIT_GROUP : 0));
    656   // Reset the index, just in case insert ended up moving it on us.
    657   index = GetIndexOfTabContents(contents);
    658 
    659   if (inherit_group && transition == PageTransition::TYPED)
    660     contents_data_.at(index)->reset_group_on_select = true;
    661 
    662   // TODO(sky): figure out why this is here and not in InsertTabContentsAt. When
    663   // here we seem to get failures in startup perf tests.
    664   // Ensure that the new TabContentsView begins at the same size as the
    665   // previous TabContentsView if it existed.  Otherwise, the initial WebKit
    666   // layout will be performed based on a width of 0 pixels, causing a
    667   // very long, narrow, inaccurate layout.  Because some scripts on pages (as
    668   // well as WebKit's anchor link location calculation) are run on the
    669   // initial layout and not recalculated later, we need to ensure the first
    670   // layout is performed with sane view dimensions even when we're opening a
    671   // new background tab.
    672   if (TabContentsWrapper* old_contents = GetSelectedTabContents()) {
    673     if ((add_types & ADD_ACTIVE) == 0) {
    674       contents->tab_contents()->view()->
    675           SizeContents(old_contents->tab_contents()->
    676                           view()->GetContainerSize());
    677       // We need to hide the contents or else we get and execute paints for
    678       // background tabs. With enough background tabs they will steal the
    679       // backing store of the visible tab causing flashing. See bug 20831.
    680       contents->tab_contents()->HideContents();
    681     }
    682   }
    683 }
    684 
    685 void TabStripModel::CloseSelectedTabs() {
    686   InternalCloseTabs(selection_model_.selected_indices(),
    687                     CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE);
    688 }
    689 
    690 void TabStripModel::SelectNextTab() {
    691   SelectRelativeTab(true);
    692 }
    693 
    694 void TabStripModel::SelectPreviousTab() {
    695   SelectRelativeTab(false);
    696 }
    697 
    698 void TabStripModel::SelectLastTab() {
    699   ActivateTabAt(count() - 1, true);
    700 }
    701 
    702 void TabStripModel::MoveTabNext() {
    703   // TODO: this likely needs to be updated for multi-selection.
    704   int new_index = std::min(active_index() + 1, count() - 1);
    705   MoveTabContentsAt(active_index(), new_index, true);
    706 }
    707 
    708 void TabStripModel::MoveTabPrevious() {
    709   // TODO: this likely needs to be updated for multi-selection.
    710   int new_index = std::max(active_index() - 1, 0);
    711   MoveTabContentsAt(active_index(), new_index, true);
    712 }
    713 
    714 // Context menu functions.
    715 bool TabStripModel::IsContextMenuCommandEnabled(
    716     int context_index, ContextMenuCommand command_id) const {
    717   DCHECK(command_id > CommandFirst && command_id < CommandLast);
    718   switch (command_id) {
    719     case CommandNewTab:
    720       return true;
    721 
    722     case CommandCloseTab:
    723       return delegate_->CanCloseTab();
    724 
    725     case CommandReload: {
    726       std::vector<int> indices = GetIndicesForCommand(context_index);
    727       for (size_t i = 0; i < indices.size(); ++i) {
    728         TabContentsWrapper* tab = GetTabContentsAt(indices[i]);
    729         if (tab && tab->tab_contents()->delegate()->CanReloadContents(
    730                 tab->tab_contents())) {
    731           return true;
    732         }
    733       }
    734       return false;
    735     }
    736 
    737     case CommandCloseOtherTabs:
    738     case CommandCloseTabsToRight:
    739       return !GetIndicesClosedByCommand(context_index, command_id).empty();
    740 
    741     case CommandDuplicate: {
    742       std::vector<int> indices = GetIndicesForCommand(context_index);
    743       for (size_t i = 0; i < indices.size(); ++i) {
    744         if (delegate_->CanDuplicateContentsAt(indices[i]))
    745           return true;
    746       }
    747       return false;
    748     }
    749 
    750     case CommandRestoreTab:
    751       return delegate_->CanRestoreTab();
    752 
    753     case CommandTogglePinned: {
    754       std::vector<int> indices = GetIndicesForCommand(context_index);
    755       for (size_t i = 0; i < indices.size(); ++i) {
    756         if (!IsAppTab(indices[i]))
    757           return true;
    758       }
    759       return false;
    760     }
    761 
    762     case CommandBookmarkAllTabs:
    763       return browser_defaults::bookmarks_enabled &&
    764           delegate_->CanBookmarkAllTabs();
    765 
    766     case CommandUseVerticalTabs:
    767       return true;
    768 
    769     case CommandSelectByDomain:
    770     case CommandSelectByOpener:
    771       return true;
    772 
    773     default:
    774       NOTREACHED();
    775   }
    776   return false;
    777 }
    778 
    779 bool TabStripModel::IsContextMenuCommandChecked(
    780     int context_index,
    781     ContextMenuCommand command_id) const {
    782   switch (command_id) {
    783     case CommandUseVerticalTabs:
    784       return delegate()->UseVerticalTabs();
    785     default:
    786       NOTREACHED();
    787       break;
    788   }
    789   return false;
    790 }
    791 
    792 void TabStripModel::ExecuteContextMenuCommand(
    793     int context_index, ContextMenuCommand command_id) {
    794   DCHECK(command_id > CommandFirst && command_id < CommandLast);
    795   switch (command_id) {
    796     case CommandNewTab:
    797       UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_NewTab"),
    798                                 profile_);
    799       delegate()->AddBlankTabAt(context_index + 1, true);
    800       break;
    801 
    802     case CommandReload: {
    803       UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_Reload"),
    804                                 profile_);
    805       std::vector<int> indices = GetIndicesForCommand(context_index);
    806       for (size_t i = 0; i < indices.size(); ++i) {
    807         TabContentsWrapper* tab = GetTabContentsAt(indices[i]);
    808         if (tab && tab->tab_contents()->delegate()->CanReloadContents(
    809                 tab->tab_contents())) {
    810           tab->controller().Reload(true);
    811         }
    812       }
    813       break;
    814     }
    815 
    816     case CommandDuplicate: {
    817       UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_Duplicate"),
    818                                 profile_);
    819       std::vector<int> indices = GetIndicesForCommand(context_index);
    820       // Copy the TabContents off as the indices will change as tabs are
    821       // duplicated.
    822       std::vector<TabContentsWrapper*> tabs;
    823       for (size_t i = 0; i < indices.size(); ++i)
    824         tabs.push_back(GetTabContentsAt(indices[i]));
    825       for (size_t i = 0; i < tabs.size(); ++i) {
    826         int index = GetIndexOfTabContents(tabs[i]);
    827         if (index != -1 && delegate_->CanDuplicateContentsAt(index))
    828           delegate_->DuplicateContentsAt(index);
    829       }
    830       break;
    831     }
    832 
    833     case CommandCloseTab: {
    834       UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_CloseTab"),
    835                                 profile_);
    836       std::vector<int> indices = GetIndicesForCommand(context_index);
    837       // Copy the TabContents off as the indices will change as we remove
    838       // things.
    839       std::vector<TabContentsWrapper*> tabs;
    840       for (size_t i = 0; i < indices.size(); ++i)
    841         tabs.push_back(GetTabContentsAt(indices[i]));
    842       for (size_t i = 0; i < tabs.size() && delegate_->CanCloseTab(); ++i) {
    843         int index = GetIndexOfTabContents(tabs[i]);
    844         if (index != -1) {
    845           CloseTabContentsAt(index,
    846                              CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE);
    847         }
    848       }
    849       break;
    850     }
    851 
    852     case CommandCloseOtherTabs: {
    853       UserMetrics::RecordAction(
    854           UserMetricsAction("TabContextMenu_CloseOtherTabs"),
    855           profile_);
    856       InternalCloseTabs(GetIndicesClosedByCommand(context_index, command_id),
    857                         CLOSE_CREATE_HISTORICAL_TAB);
    858       break;
    859     }
    860 
    861     case CommandCloseTabsToRight: {
    862       UserMetrics::RecordAction(
    863           UserMetricsAction("TabContextMenu_CloseTabsToRight"),
    864           profile_);
    865       InternalCloseTabs(GetIndicesClosedByCommand(context_index, command_id),
    866                         CLOSE_CREATE_HISTORICAL_TAB);
    867       break;
    868     }
    869 
    870     case CommandRestoreTab: {
    871       UserMetrics::RecordAction(UserMetricsAction("TabContextMenu_RestoreTab"),
    872                                 profile_);
    873       delegate_->RestoreTab();
    874       break;
    875     }
    876 
    877     case CommandTogglePinned: {
    878       UserMetrics::RecordAction(
    879           UserMetricsAction("TabContextMenu_TogglePinned"),
    880           profile_);
    881       std::vector<int> indices = GetIndicesForCommand(context_index);
    882       bool pin = WillContextMenuPin(context_index);
    883       if (pin) {
    884         for (size_t i = 0; i < indices.size(); ++i) {
    885           if (!IsAppTab(indices[i]))
    886             SetTabPinned(indices[i], true);
    887         }
    888       } else {
    889         // Unpin from the back so that the order is maintained (unpinning can
    890         // trigger moving a tab).
    891         for (size_t i = indices.size(); i > 0; --i) {
    892           if (!IsAppTab(indices[i - 1]))
    893             SetTabPinned(indices[i - 1], false);
    894         }
    895       }
    896       break;
    897     }
    898 
    899     case CommandBookmarkAllTabs: {
    900       UserMetrics::RecordAction(
    901           UserMetricsAction("TabContextMenu_BookmarkAllTabs"),
    902           profile_);
    903 
    904       delegate_->BookmarkAllTabs();
    905       break;
    906     }
    907 
    908     case CommandUseVerticalTabs: {
    909       UserMetrics::RecordAction(
    910           UserMetricsAction("TabContextMenu_UseVerticalTabs"),
    911           profile_);
    912 
    913       delegate()->ToggleUseVerticalTabs();
    914       break;
    915     }
    916 
    917     case CommandSelectByDomain:
    918     case CommandSelectByOpener: {
    919       std::vector<int> indices;
    920       if (command_id == CommandSelectByDomain)
    921         GetIndicesWithSameDomain(context_index, &indices);
    922       else
    923         GetIndicesWithSameOpener(context_index, &indices);
    924       TabStripSelectionModel selection_model;
    925       selection_model.SetSelectedIndex(context_index);
    926       for (size_t i = 0; i < indices.size(); ++i)
    927         selection_model.AddIndexToSelection(indices[i]);
    928       SetSelectionFromModel(selection_model);
    929       break;
    930     }
    931 
    932     default:
    933       NOTREACHED();
    934   }
    935 }
    936 
    937 std::vector<int> TabStripModel::GetIndicesClosedByCommand(
    938     int index,
    939     ContextMenuCommand id) const {
    940   DCHECK(ContainsIndex(index));
    941   DCHECK(id == CommandCloseTabsToRight || id == CommandCloseOtherTabs);
    942   bool is_selected = IsTabSelected(index);
    943   int start;
    944   if (id == CommandCloseTabsToRight) {
    945     if (is_selected) {
    946       start = selection_model_.selected_indices()[
    947           selection_model_.selected_indices().size() - 1] + 1;
    948     } else {
    949       start = index + 1;
    950     }
    951   } else {
    952     start = 0;
    953   }
    954   // NOTE: callers expect the vector to be sorted in descending order.
    955   std::vector<int> indices;
    956   for (int i = count() - 1; i >= start; --i) {
    957     if (i != index && !IsMiniTab(i) && (!is_selected || !IsTabSelected(i)))
    958       indices.push_back(i);
    959   }
    960   return indices;
    961 }
    962 
    963 bool TabStripModel::WillContextMenuPin(int index) {
    964   std::vector<int> indices = GetIndicesForCommand(index);
    965   // If all tabs are pinned, then we unpin, otherwise we pin.
    966   bool all_pinned = true;
    967   for (size_t i = 0; i < indices.size() && all_pinned; ++i) {
    968     if (!IsAppTab(index))  // We never change app tabs.
    969       all_pinned = IsTabPinned(indices[i]);
    970   }
    971   return !all_pinned;
    972 }
    973 
    974 ///////////////////////////////////////////////////////////////////////////////
    975 // TabStripModel, NotificationObserver implementation:
    976 
    977 void TabStripModel::Observe(NotificationType type,
    978                             const NotificationSource& source,
    979                             const NotificationDetails& details) {
    980   switch (type.value) {
    981     case NotificationType::TAB_CONTENTS_DESTROYED: {
    982       // Sometimes, on qemu, it seems like a TabContents object can be destroyed
    983       // while we still have a reference to it. We need to break this reference
    984       // here so we don't crash later.
    985       int index = GetWrapperIndex(Source<TabContents>(source).ptr());
    986       if (index != TabStripModel::kNoTab) {
    987         // Note that we only detach the contents here, not close it - it's
    988         // already been closed. We just want to undo our bookkeeping.
    989         DetachTabContentsAt(index);
    990       }
    991       break;
    992     }
    993 
    994     case NotificationType::EXTENSION_UNLOADED: {
    995       const Extension* extension =
    996           Details<UnloadedExtensionInfo>(details)->extension;
    997       // Iterate backwards as we may remove items while iterating.
    998       for (int i = count() - 1; i >= 0; i--) {
    999         TabContentsWrapper* contents = GetTabContentsAt(i);
   1000         if (contents->extension_tab_helper()->extension_app() == extension) {
   1001           // The extension an app tab was created from has been nuked. Delete
   1002           // the TabContents. Deleting a TabContents results in a notification
   1003           // of type TAB_CONTENTS_DESTROYED; we do the necessary cleanup in
   1004           // handling that notification.
   1005 
   1006           InternalCloseTab(contents, i, false);
   1007         }
   1008       }
   1009       break;
   1010     }
   1011 
   1012     default:
   1013       NOTREACHED();
   1014   }
   1015 }
   1016 
   1017 // static
   1018 bool TabStripModel::ContextMenuCommandToBrowserCommand(int cmd_id,
   1019     int* browser_cmd) {
   1020   switch (cmd_id) {
   1021     case CommandNewTab:
   1022       *browser_cmd = IDC_NEW_TAB;
   1023       break;
   1024     case CommandReload:
   1025       *browser_cmd = IDC_RELOAD;
   1026       break;
   1027     case CommandDuplicate:
   1028       *browser_cmd = IDC_DUPLICATE_TAB;
   1029       break;
   1030     case CommandCloseTab:
   1031       *browser_cmd = IDC_CLOSE_TAB;
   1032       break;
   1033     case CommandRestoreTab:
   1034       *browser_cmd = IDC_RESTORE_TAB;
   1035       break;
   1036     case CommandBookmarkAllTabs:
   1037       *browser_cmd = IDC_BOOKMARK_ALL_TABS;
   1038       break;
   1039     case CommandUseVerticalTabs:
   1040       *browser_cmd = IDC_TOGGLE_VERTICAL_TABS;
   1041       break;
   1042     default:
   1043       *browser_cmd = 0;
   1044       return false;
   1045   }
   1046 
   1047   return true;
   1048 }
   1049 
   1050 ///////////////////////////////////////////////////////////////////////////////
   1051 // TabStripModel, private:
   1052 
   1053 void TabStripModel::GetIndicesWithSameDomain(int index,
   1054                                              std::vector<int>* indices) {
   1055   TabContentsWrapper* tab = GetTabContentsAt(index);
   1056   std::string domain = tab->tab_contents()->GetURL().host();
   1057   if (domain.empty())
   1058     return;
   1059   for (int i = 0; i < count(); ++i) {
   1060     if (i == index)
   1061       continue;
   1062     if (GetTabContentsAt(i)->tab_contents()->GetURL().host() == domain)
   1063       indices->push_back(i);
   1064   }
   1065 }
   1066 
   1067 void TabStripModel::GetIndicesWithSameOpener(int index,
   1068                                              std::vector<int>* indices) {
   1069   NavigationController* opener = contents_data_[index]->group;
   1070   if (!opener) {
   1071     // If there is no group, find all tabs with the selected tab as the opener.
   1072     opener = &(GetTabContentsAt(index)->controller());
   1073     if (!opener)
   1074       return;
   1075   }
   1076   for (int i = 0; i < count(); ++i) {
   1077     if (i == index)
   1078       continue;
   1079     if (contents_data_[i]->group == opener ||
   1080         &(GetTabContentsAt(i)->controller()) == opener) {
   1081       indices->push_back(i);
   1082     }
   1083   }
   1084 }
   1085 
   1086 std::vector<int> TabStripModel::GetIndicesForCommand(int index) const {
   1087   if (!IsTabSelected(index)) {
   1088     std::vector<int> indices;
   1089     indices.push_back(index);
   1090     return indices;
   1091   }
   1092   return selection_model_.selected_indices();
   1093 }
   1094 
   1095 bool TabStripModel::IsNewTabAtEndOfTabStrip(
   1096     TabContentsWrapper* contents) const {
   1097   return LowerCaseEqualsASCII(contents->tab_contents()->GetURL().spec(),
   1098                               chrome::kChromeUINewTabURL) &&
   1099       contents == GetContentsAt(count() - 1) &&
   1100       contents->controller().entry_count() == 1;
   1101 }
   1102 
   1103 bool TabStripModel::InternalCloseTabs(const std::vector<int>& indices,
   1104                                       uint32 close_types) {
   1105   if (indices.empty())
   1106     return true;
   1107 
   1108   bool retval = true;
   1109 
   1110   // Map the indices to TabContents, that way if deleting a tab deletes other
   1111   // tabs we're ok. Crashes seem to indicate during tab deletion other tabs are
   1112   // getting removed.
   1113   std::vector<TabContentsWrapper*> tabs;
   1114   for (size_t i = 0; i < indices.size(); ++i)
   1115     tabs.push_back(GetContentsAt(indices[i]));
   1116 
   1117   // We only try the fast shutdown path if the whole browser process is *not*
   1118   // shutting down. Fast shutdown during browser termination is handled in
   1119   // BrowserShutdown.
   1120   if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID) {
   1121     // Construct a map of processes to the number of associated tabs that are
   1122     // closing.
   1123     std::map<RenderProcessHost*, size_t> processes;
   1124     for (size_t i = 0; i < indices.size(); ++i) {
   1125       if (!delegate_->CanCloseContentsAt(indices[i])) {
   1126         retval = false;
   1127         continue;
   1128       }
   1129 
   1130       TabContentsWrapper* detached_contents = GetContentsAt(indices[i]);
   1131       RenderProcessHost* process =
   1132           detached_contents->tab_contents()->GetRenderProcessHost();
   1133       std::map<RenderProcessHost*, size_t>::iterator iter =
   1134           processes.find(process);
   1135       if (iter == processes.end()) {
   1136         processes[process] = 1;
   1137       } else {
   1138         iter->second++;
   1139       }
   1140     }
   1141 
   1142     // Try to fast shutdown the tabs that can close.
   1143     for (std::map<RenderProcessHost*, size_t>::iterator iter =
   1144             processes.begin();
   1145         iter != processes.end(); ++iter) {
   1146       iter->first->FastShutdownForPageCount(iter->second);
   1147     }
   1148   }
   1149 
   1150   // We now return to our regularly scheduled shutdown procedure.
   1151   for (size_t i = 0; i < tabs.size(); ++i) {
   1152     TabContentsWrapper* detached_contents = tabs[i];
   1153     int index = GetIndexOfTabContents(detached_contents);
   1154     // Make sure we still contain the tab.
   1155     if (index == kNoTab)
   1156       continue;
   1157 
   1158     detached_contents->tab_contents()->OnCloseStarted();
   1159 
   1160     if (!delegate_->CanCloseContentsAt(index)) {
   1161       retval = false;
   1162       continue;
   1163     }
   1164 
   1165     // Update the explicitly closed state. If the unload handlers cancel the
   1166     // close the state is reset in Browser. We don't update the explicitly
   1167     // closed state if already marked as explicitly closed as unload handlers
   1168     // call back to this if the close is allowed.
   1169     if (!detached_contents->tab_contents()->closed_by_user_gesture()) {
   1170       detached_contents->tab_contents()->set_closed_by_user_gesture(
   1171           close_types & CLOSE_USER_GESTURE);
   1172     }
   1173 
   1174     if (delegate_->RunUnloadListenerBeforeClosing(detached_contents)) {
   1175       retval = false;
   1176       continue;
   1177     }
   1178 
   1179     InternalCloseTab(detached_contents, index,
   1180                      (close_types & CLOSE_CREATE_HISTORICAL_TAB) != 0);
   1181   }
   1182 
   1183   return retval;
   1184 }
   1185 
   1186 void TabStripModel::InternalCloseTab(TabContentsWrapper* contents,
   1187                                      int index,
   1188                                      bool create_historical_tabs) {
   1189   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
   1190                     TabClosingAt(this, contents, index));
   1191 
   1192   // Ask the delegate to save an entry for this tab in the historical tab
   1193   // database if applicable.
   1194   if (create_historical_tabs)
   1195     delegate_->CreateHistoricalTab(contents);
   1196 
   1197   // Deleting the TabContents will call back to us via NotificationObserver
   1198   // and detach it.
   1199   delete contents;
   1200 }
   1201 
   1202 TabContentsWrapper* TabStripModel::GetContentsAt(int index) const {
   1203   CHECK(ContainsIndex(index)) <<
   1204       "Failed to find: " << index << " in: " << count() << " entries.";
   1205   return contents_data_.at(index)->contents;
   1206 }
   1207 
   1208 void TabStripModel::NotifyTabSelectedIfChanged(TabContentsWrapper* old_contents,
   1209                                                int to_index,
   1210                                                bool user_gesture) {
   1211   TabContentsWrapper* new_contents = GetContentsAt(to_index);
   1212   if (old_contents == new_contents)
   1213     return;
   1214 
   1215   TabContentsWrapper* last_selected_contents = old_contents;
   1216   if (last_selected_contents) {
   1217     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
   1218                       TabDeselected(last_selected_contents));
   1219   }
   1220 
   1221   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
   1222                     TabSelectedAt(last_selected_contents, new_contents,
   1223                                   active_index(), user_gesture));
   1224 }
   1225 
   1226 void TabStripModel::NotifySelectionChanged(int old_selected_index) {
   1227   TabContentsWrapper* old_tab =
   1228       old_selected_index == TabStripSelectionModel::kUnselectedIndex ?
   1229       NULL : GetTabContentsAt(old_selected_index);
   1230   TabContentsWrapper* new_tab =
   1231       active_index() == TabStripSelectionModel::kUnselectedIndex ?
   1232       NULL : GetTabContentsAt(active_index());
   1233   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
   1234                     TabSelectedAt(old_tab, new_tab, active_index(), true));
   1235 }
   1236 
   1237 void TabStripModel::SelectRelativeTab(bool next) {
   1238   // This may happen during automated testing or if a user somehow buffers
   1239   // many key accelerators.
   1240   if (contents_data_.empty())
   1241     return;
   1242 
   1243   int index = active_index();
   1244   int delta = next ? 1 : -1;
   1245   index = (index + count() + delta) % count();
   1246   ActivateTabAt(index, true);
   1247 }
   1248 
   1249 void TabStripModel::MoveTabContentsAtImpl(int index,
   1250                                           int to_position,
   1251                                           bool select_after_move) {
   1252   TabContentsData* moved_data = contents_data_.at(index);
   1253   contents_data_.erase(contents_data_.begin() + index);
   1254   contents_data_.insert(contents_data_.begin() + to_position, moved_data);
   1255 
   1256   selection_model_.Move(index, to_position);
   1257   if (!selection_model_.IsSelected(select_after_move) && select_after_move) {
   1258     // TODO(sky): why doesn't this code notify observers?
   1259     selection_model_.SetSelectedIndex(to_position);
   1260   }
   1261 
   1262   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
   1263                     TabMoved(moved_data->contents, index, to_position));
   1264 }
   1265 
   1266 void TabStripModel::MoveSelectedTabsToImpl(int index,
   1267                                            size_t start,
   1268                                            size_t length) {
   1269   DCHECK(start < selection_model_.selected_indices().size() &&
   1270          start + length <= selection_model_.selected_indices().size());
   1271   size_t end = start + length;
   1272   int count_before_index = 0;
   1273   for (size_t i = start; i < end &&
   1274        selection_model_.selected_indices()[i] < index + count_before_index;
   1275        ++i) {
   1276     count_before_index++;
   1277   }
   1278 
   1279   // First move those before index. Any tabs before index end up moving in the
   1280   // selection model so we use start each time through.
   1281   int target_index = index + count_before_index;
   1282   size_t tab_index = start;
   1283   while (tab_index < end &&
   1284          selection_model_.selected_indices()[start] < index) {
   1285     MoveTabContentsAt(selection_model_.selected_indices()[start],
   1286                       target_index - 1, false);
   1287     tab_index++;
   1288   }
   1289 
   1290   // Then move those after the index. These don't result in reordering the
   1291   // selection.
   1292   while (tab_index < end) {
   1293     if (selection_model_.selected_indices()[tab_index] != target_index) {
   1294       MoveTabContentsAt(selection_model_.selected_indices()[tab_index],
   1295                         target_index, false);
   1296     }
   1297     tab_index++;
   1298     target_index++;
   1299   }
   1300 }
   1301 
   1302 // static
   1303 bool TabStripModel::OpenerMatches(const TabContentsData* data,
   1304                                   const NavigationController* opener,
   1305                                   bool use_group) {
   1306   return data->opener == opener || (use_group && data->group == opener);
   1307 }
   1308 
   1309 void TabStripModel::ForgetOpenersAndGroupsReferencing(
   1310     const NavigationController* tab) {
   1311   for (TabContentsDataVector::const_iterator i = contents_data_.begin();
   1312        i != contents_data_.end(); ++i) {
   1313     if ((*i)->group == tab)
   1314       (*i)->group = NULL;
   1315     if ((*i)->opener == tab)
   1316       (*i)->opener = NULL;
   1317   }
   1318 }
   1319