Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / tabs / tab_strip_model.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/tabs/tab_strip_model.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <string>
10
11 #include "apps/ui/web_contents_sizer.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "chrome/app/chrome_command_ids.h"
15 #include "chrome/browser/browser_shutdown.h"
16 #include "chrome/browser/defaults.h"
17 #include "chrome/browser/extensions/tab_helper.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
20 #include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
23 #include "chrome/common/url_constants.h"
24 #include "components/web_modal/popup_manager.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/user_metrics.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_observer.h"
29 using base::UserMetricsAction;
30 using content::WebContents;
31
32 namespace {
33
34 // Returns true if the specified transition is one of the types that cause the
35 // opener relationships for the tab in which the transition occurred to be
36 // forgotten. This is generally any navigation that isn't a link click (i.e.
37 // any navigation that can be considered to be the start of a new task distinct
38 // from what had previously occurred in that tab).
39 bool ShouldForgetOpenersForTransition(content::PageTransition transition) {
40   return transition == content::PAGE_TRANSITION_TYPED ||
41       transition == content::PAGE_TRANSITION_AUTO_BOOKMARK ||
42       transition == content::PAGE_TRANSITION_GENERATED ||
43       transition == content::PAGE_TRANSITION_KEYWORD ||
44       transition == content::PAGE_TRANSITION_AUTO_TOPLEVEL;
45 }
46
47 // CloseTracker is used when closing a set of WebContents. It listens for
48 // deletions of the WebContents and removes from the internal set any time one
49 // is deleted.
50 class CloseTracker {
51  public:
52   typedef std::vector<WebContents*> Contents;
53
54   explicit CloseTracker(const Contents& contents);
55   virtual ~CloseTracker();
56
57   // Returns true if there is another WebContents in the Tracker.
58   bool HasNext() const;
59
60   // Returns the next WebContents, or NULL if there are no more.
61   WebContents* Next();
62
63  private:
64   class DeletionObserver : public content::WebContentsObserver {
65    public:
66     DeletionObserver(CloseTracker* parent, WebContents* web_contents)
67         : WebContentsObserver(web_contents),
68           parent_(parent) {
69     }
70
71     // Expose web_contents() publicly.
72     using content::WebContentsObserver::web_contents;
73
74    private:
75     // WebContentsObserver:
76     virtual void WebContentsDestroyed() OVERRIDE {
77       parent_->OnWebContentsDestroyed(this);
78     }
79
80     CloseTracker* parent_;
81
82     DISALLOW_COPY_AND_ASSIGN(DeletionObserver);
83   };
84
85   void OnWebContentsDestroyed(DeletionObserver* observer);
86
87   typedef std::vector<DeletionObserver*> Observers;
88   Observers observers_;
89
90   DISALLOW_COPY_AND_ASSIGN(CloseTracker);
91 };
92
93 CloseTracker::CloseTracker(const Contents& contents) {
94   for (size_t i = 0; i < contents.size(); ++i)
95     observers_.push_back(new DeletionObserver(this, contents[i]));
96 }
97
98 CloseTracker::~CloseTracker() {
99   DCHECK(observers_.empty());
100 }
101
102 bool CloseTracker::HasNext() const {
103   return !observers_.empty();
104 }
105
106 WebContents* CloseTracker::Next() {
107   if (observers_.empty())
108     return NULL;
109
110   DeletionObserver* observer = observers_[0];
111   WebContents* web_contents = observer->web_contents();
112   observers_.erase(observers_.begin());
113   delete observer;
114   return web_contents;
115 }
116
117 void CloseTracker::OnWebContentsDestroyed(DeletionObserver* observer) {
118   Observers::iterator i =
119       std::find(observers_.begin(), observers_.end(), observer);
120   if (i != observers_.end()) {
121     delete *i;
122     observers_.erase(i);
123     return;
124   }
125   NOTREACHED() << "WebContents destroyed that wasn't in the list";
126 }
127
128 }  // namespace
129
130 ///////////////////////////////////////////////////////////////////////////////
131 // WebContentsData
132
133 // An object to hold a reference to a WebContents that is in a tabstrip, as
134 // well as other various properties it has.
135 class TabStripModel::WebContentsData : public content::WebContentsObserver {
136  public:
137   WebContentsData(TabStripModel* tab_strip_model, WebContents* a_contents);
138
139   // Changes the WebContents that this WebContentsData tracks.
140   void SetWebContents(WebContents* contents);
141   WebContents* web_contents() { return contents_; }
142
143   // Create a relationship between this WebContentsData and other
144   // WebContentses. Used to identify which WebContents to select next after
145   // one is closed.
146   WebContents* group() const { return group_; }
147   void set_group(WebContents* value) { group_ = value; }
148   WebContents* opener() const { return opener_; }
149   void set_opener(WebContents* value) { opener_ = value; }
150
151   // Alters the properties of the WebContents.
152   bool reset_group_on_select() const { return reset_group_on_select_; }
153   void set_reset_group_on_select(bool value) { reset_group_on_select_ = value; }
154   bool pinned() const { return pinned_; }
155   void set_pinned(bool value) { pinned_ = value; }
156   bool blocked() const { return blocked_; }
157   void set_blocked(bool value) { blocked_ = value; }
158   bool discarded() const { return discarded_; }
159   void set_discarded(bool value) { discarded_ = value; }
160
161  private:
162   // Make sure that if someone deletes this WebContents out from under us, it
163   // is properly removed from the tab strip.
164   virtual void WebContentsDestroyed() OVERRIDE;
165
166   // The WebContents being tracked by this WebContentsData. The
167   // WebContentsObserver does keep a reference, but when the WebContents is
168   // deleted, the WebContentsObserver reference is NULLed and thus inaccessible.
169   WebContents* contents_;
170
171   // The TabStripModel containing this WebContents.
172   TabStripModel* tab_strip_model_;
173
174   // The group is used to model a set of tabs spawned from a single parent
175   // tab. This value is preserved for a given tab as long as the tab remains
176   // navigated to the link it was initially opened at or some navigation from
177   // that page (i.e. if the user types or visits a bookmark or some other
178   // navigation within that tab, the group relationship is lost). This
179   // property can safely be used to implement features that depend on a
180   // logical group of related tabs.
181   WebContents* group_;
182
183   // The owner models the same relationship as group, except it is more
184   // easily discarded, e.g. when the user switches to a tab not part of the
185   // same group. This property is used to determine what tab to select next
186   // when one is closed.
187   WebContents* opener_;
188
189   // True if our group should be reset the moment selection moves away from
190   // this tab. This is the case for tabs opened in the foreground at the end
191   // of the TabStrip while viewing another Tab. If these tabs are closed
192   // before selection moves elsewhere, their opener is selected. But if
193   // selection shifts to _any_ tab (including their opener), the group
194   // relationship is reset to avoid confusing close sequencing.
195   bool reset_group_on_select_;
196
197   // Is the tab pinned?
198   bool pinned_;
199
200   // Is the tab interaction blocked by a modal dialog?
201   bool blocked_;
202
203   // Has the tab data been discarded to save memory?
204   bool discarded_;
205
206   DISALLOW_COPY_AND_ASSIGN(WebContentsData);
207 };
208
209 TabStripModel::WebContentsData::WebContentsData(TabStripModel* tab_strip_model,
210                                                 WebContents* contents)
211     : content::WebContentsObserver(contents),
212       contents_(contents),
213       tab_strip_model_(tab_strip_model),
214       group_(NULL),
215       opener_(NULL),
216       reset_group_on_select_(false),
217       pinned_(false),
218       blocked_(false),
219       discarded_(false) {
220 }
221
222 void TabStripModel::WebContentsData::SetWebContents(WebContents* contents) {
223   contents_ = contents;
224   Observe(contents);
225 }
226
227 void TabStripModel::WebContentsData::WebContentsDestroyed() {
228   DCHECK_EQ(contents_, web_contents());
229
230   // Note that we only detach the contents here, not close it - it's
231   // already been closed. We just want to undo our bookkeeping.
232   int index = tab_strip_model_->GetIndexOfWebContents(web_contents());
233   DCHECK_NE(TabStripModel::kNoTab, index);
234   tab_strip_model_->DetachWebContentsAt(index);
235 }
236
237 ///////////////////////////////////////////////////////////////////////////////
238 // TabStripModel, public:
239
240 TabStripModel::TabStripModel(TabStripModelDelegate* delegate, Profile* profile)
241     : delegate_(delegate),
242       profile_(profile),
243       closing_all_(false),
244       in_notify_(false),
245       weak_factory_(this) {
246   DCHECK(delegate_);
247   order_controller_.reset(new TabStripModelOrderController(this));
248 }
249
250 TabStripModel::~TabStripModel() {
251   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
252                     TabStripModelDeleted());
253   STLDeleteElements(&contents_data_);
254   order_controller_.reset();
255 }
256
257 void TabStripModel::AddObserver(TabStripModelObserver* observer) {
258   observers_.AddObserver(observer);
259 }
260
261 void TabStripModel::RemoveObserver(TabStripModelObserver* observer) {
262   observers_.RemoveObserver(observer);
263 }
264
265 bool TabStripModel::ContainsIndex(int index) const {
266   return index >= 0 && index < count();
267 }
268
269 void TabStripModel::AppendWebContents(WebContents* contents,
270                                       bool foreground) {
271   InsertWebContentsAt(count(), contents,
272                       foreground ? (ADD_INHERIT_GROUP | ADD_ACTIVE) :
273                                    ADD_NONE);
274 }
275
276 void TabStripModel::InsertWebContentsAt(int index,
277                                         WebContents* contents,
278                                         int add_types) {
279   delegate_->WillAddWebContents(contents);
280
281   bool active = add_types & ADD_ACTIVE;
282   // Force app tabs to be pinned.
283   extensions::TabHelper* extensions_tab_helper =
284       extensions::TabHelper::FromWebContents(contents);
285   bool pin = extensions_tab_helper->is_app() || add_types & ADD_PINNED;
286   index = ConstrainInsertionIndex(index, pin);
287
288   // In tab dragging situations, if the last tab in the window was detached
289   // then the user aborted the drag, we will have the |closing_all_| member
290   // set (see DetachWebContentsAt) which will mess with our mojo here. We need
291   // to clear this bit.
292   closing_all_ = false;
293
294   // Have to get the active contents before we monkey with the contents
295   // otherwise we run into problems when we try to change the active contents
296   // since the old contents and the new contents will be the same...
297   WebContents* active_contents = GetActiveWebContents();
298   WebContentsData* data = new WebContentsData(this, contents);
299   data->set_pinned(pin);
300   if ((add_types & ADD_INHERIT_GROUP) && active_contents) {
301     if (active) {
302       // Forget any existing relationships, we don't want to make things too
303       // confusing by having multiple groups active at the same time.
304       ForgetAllOpeners();
305     }
306     // Anything opened by a link we deem to have an opener.
307     data->set_group(active_contents);
308     data->set_opener(active_contents);
309   } else if ((add_types & ADD_INHERIT_OPENER) && active_contents) {
310     if (active) {
311       // Forget any existing relationships, we don't want to make things too
312       // confusing by having multiple groups active at the same time.
313       ForgetAllOpeners();
314     }
315     data->set_opener(active_contents);
316   }
317
318   // TODO(gbillock): Ask the bubble manager whether the WebContents should be
319   // blocked, or just let the bubble manager make the blocking call directly
320   // and not use this at all.
321   web_modal::PopupManager* popup_manager =
322       web_modal::PopupManager::FromWebContents(contents);
323   if (popup_manager)
324     data->set_blocked(popup_manager->IsWebModalDialogActive(contents));
325
326   contents_data_.insert(contents_data_.begin() + index, data);
327
328   selection_model_.IncrementFrom(index);
329
330   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
331                     TabInsertedAt(contents, index, active));
332   if (active) {
333     ui::ListSelectionModel new_model;
334     new_model.Copy(selection_model_);
335     new_model.SetSelectedIndex(index);
336     SetSelection(new_model, NOTIFY_DEFAULT);
337   }
338 }
339
340 WebContents* TabStripModel::ReplaceWebContentsAt(int index,
341                                                  WebContents* new_contents) {
342   delegate_->WillAddWebContents(new_contents);
343
344   DCHECK(ContainsIndex(index));
345   WebContents* old_contents = GetWebContentsAtImpl(index);
346
347   ForgetOpenersAndGroupsReferencing(old_contents);
348
349   contents_data_[index]->SetWebContents(new_contents);
350
351   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
352                     TabReplacedAt(this, old_contents, new_contents, index));
353
354   // When the active WebContents is replaced send out a selection notification
355   // too. We do this as nearly all observers need to treat a replacement of the
356   // selected contents as the selection changing.
357   if (active_index() == index) {
358     FOR_EACH_OBSERVER(
359         TabStripModelObserver,
360         observers_,
361         ActiveTabChanged(old_contents,
362                          new_contents,
363                          active_index(),
364                          TabStripModelObserver::CHANGE_REASON_REPLACED));
365   }
366   return old_contents;
367 }
368
369 WebContents* TabStripModel::DiscardWebContentsAt(int index) {
370   DCHECK(ContainsIndex(index));
371   // Do not discard active tab.
372   if (active_index() == index)
373     return NULL;
374
375   WebContents* null_contents =
376       WebContents::Create(WebContents::CreateParams(profile()));
377   WebContents* old_contents = GetWebContentsAtImpl(index);
378   // Copy over the state from the navigation controller so we preserve the
379   // back/forward history and continue to display the correct title/favicon.
380   null_contents->GetController().CopyStateFrom(old_contents->GetController());
381   // Replace the tab we're discarding with the null version.
382   ReplaceWebContentsAt(index, null_contents);
383   // Mark the tab so it will reload when we click.
384   contents_data_[index]->set_discarded(true);
385   // Discard the old tab's renderer.
386   // TODO(jamescook): This breaks script connections with other tabs.
387   // We need to find a different approach that doesn't do that, perhaps based
388   // on navigation to swappedout://.
389   delete old_contents;
390   return null_contents;
391 }
392
393 WebContents* TabStripModel::DetachWebContentsAt(int index) {
394   CHECK(!in_notify_);
395   if (contents_data_.empty())
396     return NULL;
397
398   DCHECK(ContainsIndex(index));
399
400   WebContents* removed_contents = GetWebContentsAtImpl(index);
401   bool was_selected = IsTabSelected(index);
402   int next_selected_index = order_controller_->DetermineNewSelectedIndex(index);
403   delete contents_data_[index];
404   contents_data_.erase(contents_data_.begin() + index);
405   ForgetOpenersAndGroupsReferencing(removed_contents);
406   if (empty())
407     closing_all_ = true;
408   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
409                     TabDetachedAt(removed_contents, index));
410   if (empty()) {
411     selection_model_.Clear();
412     // TabDetachedAt() might unregister observers, so send |TabStripEmpty()| in
413     // a second pass.
414     FOR_EACH_OBSERVER(TabStripModelObserver, observers_, TabStripEmpty());
415   } else {
416     int old_active = active_index();
417     selection_model_.DecrementFrom(index);
418     ui::ListSelectionModel old_model;
419     old_model.Copy(selection_model_);
420     if (index == old_active) {
421       NotifyIfTabDeactivated(removed_contents);
422       if (!selection_model_.empty()) {
423         // The active tab was removed, but there is still something selected.
424         // Move the active and anchor to the first selected index.
425         selection_model_.set_active(selection_model_.selected_indices()[0]);
426         selection_model_.set_anchor(selection_model_.active());
427       } else {
428         // The active tab was removed and nothing is selected. Reset the
429         // selection and send out notification.
430         selection_model_.SetSelectedIndex(next_selected_index);
431       }
432       NotifyIfActiveTabChanged(removed_contents, NOTIFY_DEFAULT);
433     }
434
435     // Sending notification in case the detached tab was selected. Using
436     // NotifyIfActiveOrSelectionChanged() here would not guarantee that a
437     // notification is sent even though the tab selection has changed because
438     // |old_model| is stored after calling DecrementFrom().
439     if (was_selected) {
440       FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
441                         TabSelectionChanged(this, old_model));
442     }
443   }
444   return removed_contents;
445 }
446
447 void TabStripModel::ActivateTabAt(int index, bool user_gesture) {
448   DCHECK(ContainsIndex(index));
449   ui::ListSelectionModel new_model;
450   new_model.Copy(selection_model_);
451   new_model.SetSelectedIndex(index);
452   SetSelection(new_model, user_gesture ? NOTIFY_USER_GESTURE : NOTIFY_DEFAULT);
453 }
454
455 void TabStripModel::AddTabAtToSelection(int index) {
456   DCHECK(ContainsIndex(index));
457   ui::ListSelectionModel new_model;
458   new_model.Copy(selection_model_);
459   new_model.AddIndexToSelection(index);
460   SetSelection(new_model, NOTIFY_DEFAULT);
461 }
462
463 void TabStripModel::MoveWebContentsAt(int index,
464                                       int to_position,
465                                       bool select_after_move) {
466   DCHECK(ContainsIndex(index));
467   if (index == to_position)
468     return;
469
470   int first_non_mini_tab = IndexOfFirstNonMiniTab();
471   if ((index < first_non_mini_tab && to_position >= first_non_mini_tab) ||
472       (to_position < first_non_mini_tab && index >= first_non_mini_tab)) {
473     // This would result in mini tabs mixed with non-mini tabs. We don't allow
474     // that.
475     return;
476   }
477
478   MoveWebContentsAtImpl(index, to_position, select_after_move);
479 }
480
481 void TabStripModel::MoveSelectedTabsTo(int index) {
482   int total_mini_count = IndexOfFirstNonMiniTab();
483   int selected_mini_count = 0;
484   int selected_count =
485       static_cast<int>(selection_model_.selected_indices().size());
486   for (int i = 0; i < selected_count &&
487            IsMiniTab(selection_model_.selected_indices()[i]); ++i) {
488     selected_mini_count++;
489   }
490
491   // To maintain that all mini-tabs occur before non-mini-tabs we move them
492   // first.
493   if (selected_mini_count > 0) {
494     MoveSelectedTabsToImpl(
495         std::min(total_mini_count - selected_mini_count, index), 0u,
496         selected_mini_count);
497     if (index > total_mini_count - selected_mini_count) {
498       // We're being told to drag mini-tabs to an invalid location. Adjust the
499       // index such that non-mini-tabs end up at a location as though we could
500       // move the mini-tabs to index. See description in header for more
501       // details.
502       index += selected_mini_count;
503     }
504   }
505   if (selected_mini_count == selected_count)
506     return;
507
508   // Then move the non-pinned tabs.
509   MoveSelectedTabsToImpl(std::max(index, total_mini_count),
510                          selected_mini_count,
511                          selected_count - selected_mini_count);
512 }
513
514 WebContents* TabStripModel::GetActiveWebContents() const {
515   return GetWebContentsAt(active_index());
516 }
517
518 WebContents* TabStripModel::GetWebContentsAt(int index) const {
519   if (ContainsIndex(index))
520     return GetWebContentsAtImpl(index);
521   return NULL;
522 }
523
524 int TabStripModel::GetIndexOfWebContents(const WebContents* contents) const {
525   for (size_t i = 0; i < contents_data_.size(); ++i) {
526     if (contents_data_[i]->web_contents() == contents)
527       return i;
528   }
529   return kNoTab;
530 }
531
532 void TabStripModel::UpdateWebContentsStateAt(int index,
533     TabStripModelObserver::TabChangeType change_type) {
534   DCHECK(ContainsIndex(index));
535
536   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
537       TabChangedAt(GetWebContentsAtImpl(index), index, change_type));
538 }
539
540 void TabStripModel::CloseAllTabs() {
541   // Set state so that observers can adjust their behavior to suit this
542   // specific condition when CloseWebContentsAt causes a flurry of
543   // Close/Detach/Select notifications to be sent.
544   closing_all_ = true;
545   std::vector<int> closing_tabs;
546   for (int i = count() - 1; i >= 0; --i)
547     closing_tabs.push_back(i);
548   InternalCloseTabs(closing_tabs, CLOSE_CREATE_HISTORICAL_TAB);
549 }
550
551 bool TabStripModel::CloseWebContentsAt(int index, uint32 close_types) {
552   DCHECK(ContainsIndex(index));
553   std::vector<int> closing_tabs;
554   closing_tabs.push_back(index);
555   return InternalCloseTabs(closing_tabs, close_types);
556 }
557
558 bool TabStripModel::TabsAreLoading() const {
559   for (WebContentsDataVector::const_iterator iter = contents_data_.begin();
560        iter != contents_data_.end(); ++iter) {
561     if ((*iter)->web_contents()->IsLoading())
562       return true;
563   }
564   return false;
565 }
566
567 WebContents* TabStripModel::GetOpenerOfWebContentsAt(int index) {
568   DCHECK(ContainsIndex(index));
569   return contents_data_[index]->opener();
570 }
571
572 void TabStripModel::SetOpenerOfWebContentsAt(int index,
573                                              WebContents* opener) {
574   DCHECK(ContainsIndex(index));
575   DCHECK(opener);
576   contents_data_[index]->set_opener(opener);
577 }
578
579 int TabStripModel::GetIndexOfNextWebContentsOpenedBy(const WebContents* opener,
580                                                      int start_index,
581                                                      bool use_group) const {
582   DCHECK(opener);
583   DCHECK(ContainsIndex(start_index));
584
585   // Check tabs after start_index first.
586   for (int i = start_index + 1; i < count(); ++i) {
587     if (OpenerMatches(contents_data_[i], opener, use_group))
588       return i;
589   }
590   // Then check tabs before start_index, iterating backwards.
591   for (int i = start_index - 1; i >= 0; --i) {
592     if (OpenerMatches(contents_data_[i], opener, use_group))
593       return i;
594   }
595   return kNoTab;
596 }
597
598 int TabStripModel::GetIndexOfLastWebContentsOpenedBy(const WebContents* opener,
599                                                      int start_index) const {
600   DCHECK(opener);
601   DCHECK(ContainsIndex(start_index));
602
603   for (int i = contents_data_.size() - 1; i > start_index; --i) {
604     if (contents_data_[i]->opener() == opener)
605       return i;
606   }
607   return kNoTab;
608 }
609
610 void TabStripModel::TabNavigating(WebContents* contents,
611                                   content::PageTransition transition) {
612   if (ShouldForgetOpenersForTransition(transition)) {
613     // Don't forget the openers if this tab is a New Tab page opened at the
614     // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
615     // navigation of one of these transition types before resetting the
616     // opener relationships (this allows for the use case of opening a new
617     // tab to do a quick look-up of something while viewing a tab earlier in
618     // the strip). We can make this heuristic more permissive if need be.
619     if (!IsNewTabAtEndOfTabStrip(contents)) {
620       // If the user navigates the current tab to another page in any way
621       // other than by clicking a link, we want to pro-actively forget all
622       // TabStrip opener relationships since we assume they're beginning a
623       // different task by reusing the current tab.
624       ForgetAllOpeners();
625       // In this specific case we also want to reset the group relationship,
626       // since it is now technically invalid.
627       ForgetGroup(contents);
628     }
629   }
630 }
631
632 void TabStripModel::ForgetAllOpeners() {
633   // Forget all opener memories so we don't do anything weird with tab
634   // re-selection ordering.
635   for (WebContentsDataVector::const_iterator iter = contents_data_.begin();
636        iter != contents_data_.end(); ++iter)
637     (*iter)->set_opener(NULL);
638 }
639
640 void TabStripModel::ForgetGroup(WebContents* contents) {
641   int index = GetIndexOfWebContents(contents);
642   DCHECK(ContainsIndex(index));
643   contents_data_[index]->set_group(NULL);
644   contents_data_[index]->set_opener(NULL);
645 }
646
647 bool TabStripModel::ShouldResetGroupOnSelect(WebContents* contents) const {
648   int index = GetIndexOfWebContents(contents);
649   DCHECK(ContainsIndex(index));
650   return contents_data_[index]->reset_group_on_select();
651 }
652
653 void TabStripModel::SetTabBlocked(int index, bool blocked) {
654   DCHECK(ContainsIndex(index));
655   if (contents_data_[index]->blocked() == blocked)
656     return;
657   contents_data_[index]->set_blocked(blocked);
658   FOR_EACH_OBSERVER(
659       TabStripModelObserver, observers_,
660       TabBlockedStateChanged(contents_data_[index]->web_contents(),
661                              index));
662 }
663
664 void TabStripModel::SetTabPinned(int index, bool pinned) {
665   DCHECK(ContainsIndex(index));
666   if (contents_data_[index]->pinned() == pinned)
667     return;
668
669   if (IsAppTab(index)) {
670     if (!pinned) {
671       // App tabs should always be pinned.
672       NOTREACHED();
673       return;
674     }
675     // Changing the pinned state of an app tab doesn't affect its mini-tab
676     // status.
677     contents_data_[index]->set_pinned(pinned);
678   } else {
679     // The tab is not an app tab, its position may have to change as the
680     // mini-tab state is changing.
681     int non_mini_tab_index = IndexOfFirstNonMiniTab();
682     contents_data_[index]->set_pinned(pinned);
683     if (pinned && index != non_mini_tab_index) {
684       MoveWebContentsAtImpl(index, non_mini_tab_index, false);
685       index = non_mini_tab_index;
686     } else if (!pinned && index + 1 != non_mini_tab_index) {
687       MoveWebContentsAtImpl(index, non_mini_tab_index - 1, false);
688       index = non_mini_tab_index - 1;
689     }
690
691     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
692                       TabMiniStateChanged(contents_data_[index]->web_contents(),
693                                           index));
694   }
695
696   // else: the tab was at the boundary and its position doesn't need to change.
697   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
698                     TabPinnedStateChanged(contents_data_[index]->web_contents(),
699                                           index));
700 }
701
702 bool TabStripModel::IsTabPinned(int index) const {
703   DCHECK(ContainsIndex(index));
704   return contents_data_[index]->pinned();
705 }
706
707 bool TabStripModel::IsMiniTab(int index) const {
708   return IsTabPinned(index) || IsAppTab(index);
709 }
710
711 bool TabStripModel::IsAppTab(int index) const {
712   WebContents* contents = GetWebContentsAt(index);
713   return contents && extensions::TabHelper::FromWebContents(contents)->is_app();
714 }
715
716 bool TabStripModel::IsTabBlocked(int index) const {
717   return contents_data_[index]->blocked();
718 }
719
720 bool TabStripModel::IsTabDiscarded(int index) const {
721   return contents_data_[index]->discarded();
722 }
723
724 int TabStripModel::IndexOfFirstNonMiniTab() const {
725   for (size_t i = 0; i < contents_data_.size(); ++i) {
726     if (!IsMiniTab(static_cast<int>(i)))
727       return static_cast<int>(i);
728   }
729   // No mini-tabs.
730   return count();
731 }
732
733 int TabStripModel::ConstrainInsertionIndex(int index, bool mini_tab) {
734   return mini_tab ? std::min(std::max(0, index), IndexOfFirstNonMiniTab()) :
735       std::min(count(), std::max(index, IndexOfFirstNonMiniTab()));
736 }
737
738 void TabStripModel::ExtendSelectionTo(int index) {
739   DCHECK(ContainsIndex(index));
740   ui::ListSelectionModel new_model;
741   new_model.Copy(selection_model_);
742   new_model.SetSelectionFromAnchorTo(index);
743   SetSelection(new_model, NOTIFY_DEFAULT);
744 }
745
746 void TabStripModel::ToggleSelectionAt(int index) {
747   DCHECK(ContainsIndex(index));
748   ui::ListSelectionModel new_model;
749   new_model.Copy(selection_model());
750   if (selection_model_.IsSelected(index)) {
751     if (selection_model_.size() == 1) {
752       // One tab must be selected and this tab is currently selected so we can't
753       // unselect it.
754       return;
755     }
756     new_model.RemoveIndexFromSelection(index);
757     new_model.set_anchor(index);
758     if (new_model.active() == index ||
759         new_model.active() == ui::ListSelectionModel::kUnselectedIndex)
760       new_model.set_active(new_model.selected_indices()[0]);
761   } else {
762     new_model.AddIndexToSelection(index);
763     new_model.set_anchor(index);
764     new_model.set_active(index);
765   }
766   SetSelection(new_model, NOTIFY_DEFAULT);
767 }
768
769 void TabStripModel::AddSelectionFromAnchorTo(int index) {
770   ui::ListSelectionModel new_model;
771   new_model.Copy(selection_model_);
772   new_model.AddSelectionFromAnchorTo(index);
773   SetSelection(new_model, NOTIFY_DEFAULT);
774 }
775
776 bool TabStripModel::IsTabSelected(int index) const {
777   DCHECK(ContainsIndex(index));
778   return selection_model_.IsSelected(index);
779 }
780
781 void TabStripModel::SetSelectionFromModel(
782     const ui::ListSelectionModel& source) {
783   DCHECK_NE(ui::ListSelectionModel::kUnselectedIndex, source.active());
784   SetSelection(source, NOTIFY_DEFAULT);
785 }
786
787 void TabStripModel::AddWebContents(WebContents* contents,
788                                    int index,
789                                    content::PageTransition transition,
790                                    int add_types) {
791   // If the newly-opened tab is part of the same task as the parent tab, we want
792   // to inherit the parent's "group" attribute, so that if this tab is then
793   // closed we'll jump back to the parent tab.
794   bool inherit_group = (add_types & ADD_INHERIT_GROUP) == ADD_INHERIT_GROUP;
795
796   if (transition == content::PAGE_TRANSITION_LINK &&
797       (add_types & ADD_FORCE_INDEX) == 0) {
798     // We assume tabs opened via link clicks are part of the same task as their
799     // parent.  Note that when |force_index| is true (e.g. when the user
800     // drag-and-drops a link to the tab strip), callers aren't really handling
801     // link clicks, they just want to score the navigation like a link click in
802     // the history backend, so we don't inherit the group in this case.
803     index = order_controller_->DetermineInsertionIndex(transition,
804                                                        add_types & ADD_ACTIVE);
805     inherit_group = true;
806   } else {
807     // For all other types, respect what was passed to us, normalizing -1s and
808     // values that are too large.
809     if (index < 0 || index > count())
810       index = count();
811   }
812
813   if (transition == content::PAGE_TRANSITION_TYPED && index == count()) {
814     // Also, any tab opened at the end of the TabStrip with a "TYPED"
815     // transition inherit group as well. This covers the cases where the user
816     // creates a New Tab (e.g. Ctrl+T, or clicks the New Tab button), or types
817     // in the address bar and presses Alt+Enter. This allows for opening a new
818     // Tab to quickly look up something. When this Tab is closed, the old one
819     // is re-selected, not the next-adjacent.
820     inherit_group = true;
821   }
822   InsertWebContentsAt(index, contents,
823                       add_types | (inherit_group ? ADD_INHERIT_GROUP : 0));
824   // Reset the index, just in case insert ended up moving it on us.
825   index = GetIndexOfWebContents(contents);
826
827   if (inherit_group && transition == content::PAGE_TRANSITION_TYPED)
828     contents_data_[index]->set_reset_group_on_select(true);
829
830   // TODO(sky): figure out why this is here and not in InsertWebContentsAt. When
831   // here we seem to get failures in startup perf tests.
832   // Ensure that the new WebContentsView begins at the same size as the
833   // previous WebContentsView if it existed.  Otherwise, the initial WebKit
834   // layout will be performed based on a width of 0 pixels, causing a
835   // very long, narrow, inaccurate layout.  Because some scripts on pages (as
836   // well as WebKit's anchor link location calculation) are run on the
837   // initial layout and not recalculated later, we need to ensure the first
838   // layout is performed with sane view dimensions even when we're opening a
839   // new background tab.
840   if (WebContents* old_contents = GetActiveWebContents()) {
841     if ((add_types & ADD_ACTIVE) == 0) {
842       apps::ResizeWebContents(contents,
843                               old_contents->GetContainerBounds().size());
844     }
845   }
846 }
847
848 void TabStripModel::CloseSelectedTabs() {
849   InternalCloseTabs(selection_model_.selected_indices(),
850                     CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE);
851 }
852
853 void TabStripModel::SelectNextTab() {
854   SelectRelativeTab(true);
855 }
856
857 void TabStripModel::SelectPreviousTab() {
858   SelectRelativeTab(false);
859 }
860
861 void TabStripModel::SelectLastTab() {
862   ActivateTabAt(count() - 1, true);
863 }
864
865 void TabStripModel::MoveTabNext() {
866   // TODO: this likely needs to be updated for multi-selection.
867   int new_index = std::min(active_index() + 1, count() - 1);
868   MoveWebContentsAt(active_index(), new_index, true);
869 }
870
871 void TabStripModel::MoveTabPrevious() {
872   // TODO: this likely needs to be updated for multi-selection.
873   int new_index = std::max(active_index() - 1, 0);
874   MoveWebContentsAt(active_index(), new_index, true);
875 }
876
877 // Context menu functions.
878 bool TabStripModel::IsContextMenuCommandEnabled(
879     int context_index, ContextMenuCommand command_id) const {
880   DCHECK(command_id > CommandFirst && command_id < CommandLast);
881   switch (command_id) {
882     case CommandNewTab:
883     case CommandCloseTab:
884       return true;
885
886     case CommandReload: {
887       std::vector<int> indices = GetIndicesForCommand(context_index);
888       for (size_t i = 0; i < indices.size(); ++i) {
889         WebContents* tab = GetWebContentsAt(indices[i]);
890         if (tab) {
891           CoreTabHelperDelegate* core_delegate =
892               CoreTabHelper::FromWebContents(tab)->delegate();
893           if (!core_delegate || core_delegate->CanReloadContents(tab))
894             return true;
895         }
896       }
897       return false;
898     }
899
900     case CommandCloseOtherTabs:
901     case CommandCloseTabsToRight:
902       return !GetIndicesClosedByCommand(context_index, command_id).empty();
903
904     case CommandDuplicate: {
905       std::vector<int> indices = GetIndicesForCommand(context_index);
906       for (size_t i = 0; i < indices.size(); ++i) {
907         if (delegate_->CanDuplicateContentsAt(indices[i]))
908           return true;
909       }
910       return false;
911     }
912
913     case CommandRestoreTab:
914       return delegate_->GetRestoreTabType() !=
915           TabStripModelDelegate::RESTORE_NONE;
916
917     case CommandTogglePinned: {
918       std::vector<int> indices = GetIndicesForCommand(context_index);
919       for (size_t i = 0; i < indices.size(); ++i) {
920         if (!IsAppTab(indices[i]))
921           return true;
922       }
923       return false;
924     }
925
926     case CommandBookmarkAllTabs:
927       return browser_defaults::bookmarks_enabled &&
928           delegate_->CanBookmarkAllTabs();
929
930     case CommandSelectByDomain:
931     case CommandSelectByOpener:
932       return true;
933
934     default:
935       NOTREACHED();
936   }
937   return false;
938 }
939
940 void TabStripModel::ExecuteContextMenuCommand(
941     int context_index, ContextMenuCommand command_id) {
942   DCHECK(command_id > CommandFirst && command_id < CommandLast);
943   switch (command_id) {
944     case CommandNewTab:
945       content::RecordAction(UserMetricsAction("TabContextMenu_NewTab"));
946       UMA_HISTOGRAM_ENUMERATION("Tab.NewTab",
947                                 TabStripModel::NEW_TAB_CONTEXT_MENU,
948                                 TabStripModel::NEW_TAB_ENUM_COUNT);
949       delegate()->AddTabAt(GURL(), context_index + 1, true);
950       break;
951
952     case CommandReload: {
953       content::RecordAction(UserMetricsAction("TabContextMenu_Reload"));
954       std::vector<int> indices = GetIndicesForCommand(context_index);
955       for (size_t i = 0; i < indices.size(); ++i) {
956         WebContents* tab = GetWebContentsAt(indices[i]);
957         if (tab) {
958           CoreTabHelperDelegate* core_delegate =
959               CoreTabHelper::FromWebContents(tab)->delegate();
960           if (!core_delegate || core_delegate->CanReloadContents(tab))
961             tab->GetController().Reload(true);
962         }
963       }
964       break;
965     }
966
967     case CommandDuplicate: {
968       content::RecordAction(UserMetricsAction("TabContextMenu_Duplicate"));
969       std::vector<int> indices = GetIndicesForCommand(context_index);
970       // Copy the WebContents off as the indices will change as tabs are
971       // duplicated.
972       std::vector<WebContents*> tabs;
973       for (size_t i = 0; i < indices.size(); ++i)
974         tabs.push_back(GetWebContentsAt(indices[i]));
975       for (size_t i = 0; i < tabs.size(); ++i) {
976         int index = GetIndexOfWebContents(tabs[i]);
977         if (index != -1 && delegate_->CanDuplicateContentsAt(index))
978           delegate_->DuplicateContentsAt(index);
979       }
980       break;
981     }
982
983     case CommandCloseTab: {
984       content::RecordAction(UserMetricsAction("TabContextMenu_CloseTab"));
985       InternalCloseTabs(GetIndicesForCommand(context_index),
986                         CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE);
987       break;
988     }
989
990     case CommandCloseOtherTabs: {
991       content::RecordAction(
992           UserMetricsAction("TabContextMenu_CloseOtherTabs"));
993       InternalCloseTabs(GetIndicesClosedByCommand(context_index, command_id),
994                         CLOSE_CREATE_HISTORICAL_TAB);
995       break;
996     }
997
998     case CommandCloseTabsToRight: {
999       content::RecordAction(
1000           UserMetricsAction("TabContextMenu_CloseTabsToRight"));
1001       InternalCloseTabs(GetIndicesClosedByCommand(context_index, command_id),
1002                         CLOSE_CREATE_HISTORICAL_TAB);
1003       break;
1004     }
1005
1006     case CommandRestoreTab: {
1007       content::RecordAction(UserMetricsAction("TabContextMenu_RestoreTab"));
1008       delegate_->RestoreTab();
1009       break;
1010     }
1011
1012     case CommandTogglePinned: {
1013       content::RecordAction(
1014           UserMetricsAction("TabContextMenu_TogglePinned"));
1015       std::vector<int> indices = GetIndicesForCommand(context_index);
1016       bool pin = WillContextMenuPin(context_index);
1017       if (pin) {
1018         for (size_t i = 0; i < indices.size(); ++i) {
1019           if (!IsAppTab(indices[i]))
1020             SetTabPinned(indices[i], true);
1021         }
1022       } else {
1023         // Unpin from the back so that the order is maintained (unpinning can
1024         // trigger moving a tab).
1025         for (size_t i = indices.size(); i > 0; --i) {
1026           if (!IsAppTab(indices[i - 1]))
1027             SetTabPinned(indices[i - 1], false);
1028         }
1029       }
1030       break;
1031     }
1032
1033     case CommandBookmarkAllTabs: {
1034       content::RecordAction(
1035           UserMetricsAction("TabContextMenu_BookmarkAllTabs"));
1036
1037       delegate_->BookmarkAllTabs();
1038       break;
1039     }
1040
1041     case CommandSelectByDomain:
1042     case CommandSelectByOpener: {
1043       std::vector<int> indices;
1044       if (command_id == CommandSelectByDomain)
1045         GetIndicesWithSameDomain(context_index, &indices);
1046       else
1047         GetIndicesWithSameOpener(context_index, &indices);
1048       ui::ListSelectionModel selection_model;
1049       selection_model.SetSelectedIndex(context_index);
1050       for (size_t i = 0; i < indices.size(); ++i)
1051         selection_model.AddIndexToSelection(indices[i]);
1052       SetSelectionFromModel(selection_model);
1053       break;
1054     }
1055
1056     default:
1057       NOTREACHED();
1058   }
1059 }
1060
1061 std::vector<int> TabStripModel::GetIndicesClosedByCommand(
1062     int index,
1063     ContextMenuCommand id) const {
1064   DCHECK(ContainsIndex(index));
1065   DCHECK(id == CommandCloseTabsToRight || id == CommandCloseOtherTabs);
1066   bool is_selected = IsTabSelected(index);
1067   int start;
1068   if (id == CommandCloseTabsToRight) {
1069     if (is_selected) {
1070       start = selection_model_.selected_indices()[
1071           selection_model_.selected_indices().size() - 1] + 1;
1072     } else {
1073       start = index + 1;
1074     }
1075   } else {
1076     start = 0;
1077   }
1078   // NOTE: callers expect the vector to be sorted in descending order.
1079   std::vector<int> indices;
1080   for (int i = count() - 1; i >= start; --i) {
1081     if (i != index && !IsMiniTab(i) && (!is_selected || !IsTabSelected(i)))
1082       indices.push_back(i);
1083   }
1084   return indices;
1085 }
1086
1087 bool TabStripModel::WillContextMenuPin(int index) {
1088   std::vector<int> indices = GetIndicesForCommand(index);
1089   // If all tabs are pinned, then we unpin, otherwise we pin.
1090   bool all_pinned = true;
1091   for (size_t i = 0; i < indices.size() && all_pinned; ++i) {
1092     if (!IsAppTab(index))  // We never change app tabs.
1093       all_pinned = IsTabPinned(indices[i]);
1094   }
1095   return !all_pinned;
1096 }
1097
1098 // static
1099 bool TabStripModel::ContextMenuCommandToBrowserCommand(int cmd_id,
1100                                                        int* browser_cmd) {
1101   switch (cmd_id) {
1102     case CommandNewTab:
1103       *browser_cmd = IDC_NEW_TAB;
1104       break;
1105     case CommandReload:
1106       *browser_cmd = IDC_RELOAD;
1107       break;
1108     case CommandDuplicate:
1109       *browser_cmd = IDC_DUPLICATE_TAB;
1110       break;
1111     case CommandCloseTab:
1112       *browser_cmd = IDC_CLOSE_TAB;
1113       break;
1114     case CommandRestoreTab:
1115       *browser_cmd = IDC_RESTORE_TAB;
1116       break;
1117     case CommandBookmarkAllTabs:
1118       *browser_cmd = IDC_BOOKMARK_ALL_TABS;
1119       break;
1120     default:
1121       *browser_cmd = 0;
1122       return false;
1123   }
1124
1125   return true;
1126 }
1127
1128 ///////////////////////////////////////////////////////////////////////////////
1129 // TabStripModel, private:
1130
1131 std::vector<WebContents*> TabStripModel::GetWebContentsFromIndices(
1132     const std::vector<int>& indices) const {
1133   std::vector<WebContents*> contents;
1134   for (size_t i = 0; i < indices.size(); ++i)
1135     contents.push_back(GetWebContentsAtImpl(indices[i]));
1136   return contents;
1137 }
1138
1139 void TabStripModel::GetIndicesWithSameDomain(int index,
1140                                              std::vector<int>* indices) {
1141   std::string domain = GetWebContentsAt(index)->GetURL().host();
1142   if (domain.empty())
1143     return;
1144   for (int i = 0; i < count(); ++i) {
1145     if (i == index)
1146       continue;
1147     if (GetWebContentsAt(i)->GetURL().host() == domain)
1148       indices->push_back(i);
1149   }
1150 }
1151
1152 void TabStripModel::GetIndicesWithSameOpener(int index,
1153                                              std::vector<int>* indices) {
1154   WebContents* opener = contents_data_[index]->group();
1155   if (!opener) {
1156     // If there is no group, find all tabs with the selected tab as the opener.
1157     opener = GetWebContentsAt(index);
1158     if (!opener)
1159       return;
1160   }
1161   for (int i = 0; i < count(); ++i) {
1162     if (i == index)
1163       continue;
1164     if (contents_data_[i]->group() == opener ||
1165         GetWebContentsAtImpl(i) == opener) {
1166       indices->push_back(i);
1167     }
1168   }
1169 }
1170
1171 std::vector<int> TabStripModel::GetIndicesForCommand(int index) const {
1172   if (!IsTabSelected(index)) {
1173     std::vector<int> indices;
1174     indices.push_back(index);
1175     return indices;
1176   }
1177   return selection_model_.selected_indices();
1178 }
1179
1180 bool TabStripModel::IsNewTabAtEndOfTabStrip(WebContents* contents) const {
1181   const GURL& url = contents->GetURL();
1182   return url.SchemeIs(content::kChromeUIScheme) &&
1183          url.host() == chrome::kChromeUINewTabHost &&
1184          contents == GetWebContentsAtImpl(count() - 1) &&
1185          contents->GetController().GetEntryCount() == 1;
1186 }
1187
1188 bool TabStripModel::InternalCloseTabs(const std::vector<int>& indices,
1189                                       uint32 close_types) {
1190   if (indices.empty())
1191     return true;
1192
1193   CloseTracker close_tracker(GetWebContentsFromIndices(indices));
1194
1195   base::WeakPtr<TabStripModel> ref(weak_factory_.GetWeakPtr());
1196   const bool closing_all = indices.size() == contents_data_.size();
1197   if (closing_all)
1198     FOR_EACH_OBSERVER(TabStripModelObserver, observers_, WillCloseAllTabs());
1199
1200   // We only try the fast shutdown path if the whole browser process is *not*
1201   // shutting down. Fast shutdown during browser termination is handled in
1202   // BrowserShutdown.
1203   if (browser_shutdown::GetShutdownType() == browser_shutdown::NOT_VALID) {
1204     // Construct a map of processes to the number of associated tabs that are
1205     // closing.
1206     std::map<content::RenderProcessHost*, size_t> processes;
1207     for (size_t i = 0; i < indices.size(); ++i) {
1208       WebContents* closing_contents = GetWebContentsAtImpl(indices[i]);
1209       if (delegate_->ShouldRunUnloadListenerBeforeClosing(closing_contents))
1210         continue;
1211       content::RenderProcessHost* process =
1212           closing_contents->GetRenderProcessHost();
1213       ++processes[process];
1214     }
1215
1216     // Try to fast shutdown the tabs that can close.
1217     for (std::map<content::RenderProcessHost*, size_t>::iterator iter =
1218          processes.begin(); iter != processes.end(); ++iter) {
1219       iter->first->FastShutdownForPageCount(iter->second);
1220     }
1221   }
1222
1223   // We now return to our regularly scheduled shutdown procedure.
1224   bool retval = true;
1225   while (close_tracker.HasNext()) {
1226     WebContents* closing_contents = close_tracker.Next();
1227     int index = GetIndexOfWebContents(closing_contents);
1228     // Make sure we still contain the tab.
1229     if (index == kNoTab)
1230       continue;
1231
1232     CoreTabHelper* core_tab_helper =
1233         CoreTabHelper::FromWebContents(closing_contents);
1234     core_tab_helper->OnCloseStarted();
1235
1236     // Update the explicitly closed state. If the unload handlers cancel the
1237     // close the state is reset in Browser. We don't update the explicitly
1238     // closed state if already marked as explicitly closed as unload handlers
1239     // call back to this if the close is allowed.
1240     if (!closing_contents->GetClosedByUserGesture()) {
1241       closing_contents->SetClosedByUserGesture(
1242           close_types & CLOSE_USER_GESTURE);
1243     }
1244
1245     if (delegate_->RunUnloadListenerBeforeClosing(closing_contents)) {
1246       retval = false;
1247       continue;
1248     }
1249
1250     InternalCloseTab(closing_contents, index,
1251                      (close_types & CLOSE_CREATE_HISTORICAL_TAB) != 0);
1252   }
1253
1254   if (ref && closing_all && !retval) {
1255     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1256                       CloseAllTabsCanceled());
1257   }
1258
1259   return retval;
1260 }
1261
1262 void TabStripModel::InternalCloseTab(WebContents* contents,
1263                                      int index,
1264                                      bool create_historical_tabs) {
1265   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1266                     TabClosingAt(this, contents, index));
1267
1268   // Ask the delegate to save an entry for this tab in the historical tab
1269   // database if applicable.
1270   if (create_historical_tabs)
1271     delegate_->CreateHistoricalTab(contents);
1272
1273   // Deleting the WebContents will call back to us via
1274   // WebContentsData::WebContentsDestroyed and detach it.
1275   delete contents;
1276 }
1277
1278 WebContents* TabStripModel::GetWebContentsAtImpl(int index) const {
1279   CHECK(ContainsIndex(index)) <<
1280       "Failed to find: " << index << " in: " << count() << " entries.";
1281   return contents_data_[index]->web_contents();
1282 }
1283
1284 void TabStripModel::NotifyIfTabDeactivated(WebContents* contents) {
1285   if (contents) {
1286     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1287                       TabDeactivated(contents));
1288   }
1289 }
1290
1291 void TabStripModel::NotifyIfActiveTabChanged(WebContents* old_contents,
1292                                              NotifyTypes notify_types) {
1293   WebContents* new_contents = GetWebContentsAtImpl(active_index());
1294   if (old_contents != new_contents) {
1295     int reason = notify_types == NOTIFY_USER_GESTURE
1296                  ? TabStripModelObserver::CHANGE_REASON_USER_GESTURE
1297                  : TabStripModelObserver::CHANGE_REASON_NONE;
1298     CHECK(!in_notify_);
1299     in_notify_ = true;
1300     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1301         ActiveTabChanged(old_contents,
1302                          new_contents,
1303                          active_index(),
1304                          reason));
1305     in_notify_ = false;
1306     // Activating a discarded tab reloads it, so it is no longer discarded.
1307     contents_data_[active_index()]->set_discarded(false);
1308   }
1309 }
1310
1311 void TabStripModel::NotifyIfActiveOrSelectionChanged(
1312     WebContents* old_contents,
1313     NotifyTypes notify_types,
1314     const ui::ListSelectionModel& old_model) {
1315   NotifyIfActiveTabChanged(old_contents, notify_types);
1316
1317   if (!selection_model().Equals(old_model)) {
1318     FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1319                       TabSelectionChanged(this, old_model));
1320   }
1321 }
1322
1323 void TabStripModel::SetSelection(
1324     const ui::ListSelectionModel& new_model,
1325     NotifyTypes notify_types) {
1326   WebContents* old_contents = GetActiveWebContents();
1327   ui::ListSelectionModel old_model;
1328   old_model.Copy(selection_model_);
1329   if (new_model.active() != selection_model_.active())
1330     NotifyIfTabDeactivated(old_contents);
1331   selection_model_.Copy(new_model);
1332   NotifyIfActiveOrSelectionChanged(old_contents, notify_types, old_model);
1333 }
1334
1335 void TabStripModel::SelectRelativeTab(bool next) {
1336   // This may happen during automated testing or if a user somehow buffers
1337   // many key accelerators.
1338   if (contents_data_.empty())
1339     return;
1340
1341   int index = active_index();
1342   int delta = next ? 1 : -1;
1343   index = (index + count() + delta) % count();
1344   ActivateTabAt(index, true);
1345 }
1346
1347 void TabStripModel::MoveWebContentsAtImpl(int index,
1348                                           int to_position,
1349                                           bool select_after_move) {
1350   WebContentsData* moved_data = contents_data_[index];
1351   contents_data_.erase(contents_data_.begin() + index);
1352   contents_data_.insert(contents_data_.begin() + to_position, moved_data);
1353
1354   selection_model_.Move(index, to_position);
1355   if (!selection_model_.IsSelected(select_after_move) && select_after_move) {
1356     // TODO(sky): why doesn't this code notify observers?
1357     selection_model_.SetSelectedIndex(to_position);
1358   }
1359
1360   ForgetOpenersAndGroupsReferencing(moved_data->web_contents());
1361
1362   FOR_EACH_OBSERVER(TabStripModelObserver, observers_,
1363                     TabMoved(moved_data->web_contents(), index, to_position));
1364 }
1365
1366 void TabStripModel::MoveSelectedTabsToImpl(int index,
1367                                            size_t start,
1368                                            size_t length) {
1369   DCHECK(start < selection_model_.selected_indices().size() &&
1370          start + length <= selection_model_.selected_indices().size());
1371   size_t end = start + length;
1372   int count_before_index = 0;
1373   for (size_t i = start; i < end &&
1374        selection_model_.selected_indices()[i] < index + count_before_index;
1375        ++i) {
1376     count_before_index++;
1377   }
1378
1379   // First move those before index. Any tabs before index end up moving in the
1380   // selection model so we use start each time through.
1381   int target_index = index + count_before_index;
1382   size_t tab_index = start;
1383   while (tab_index < end &&
1384          selection_model_.selected_indices()[start] < index) {
1385     MoveWebContentsAt(selection_model_.selected_indices()[start],
1386                       target_index - 1, false);
1387     tab_index++;
1388   }
1389
1390   // Then move those after the index. These don't result in reordering the
1391   // selection.
1392   while (tab_index < end) {
1393     if (selection_model_.selected_indices()[tab_index] != target_index) {
1394       MoveWebContentsAt(selection_model_.selected_indices()[tab_index],
1395                         target_index, false);
1396     }
1397     tab_index++;
1398     target_index++;
1399   }
1400 }
1401
1402 // static
1403 bool TabStripModel::OpenerMatches(const WebContentsData* data,
1404                                   const WebContents* opener,
1405                                   bool use_group) {
1406   return data->opener() == opener || (use_group && data->group() == opener);
1407 }
1408
1409 void TabStripModel::ForgetOpenersAndGroupsReferencing(
1410     const WebContents* tab) {
1411   for (WebContentsDataVector::const_iterator i = contents_data_.begin();
1412        i != contents_data_.end(); ++i) {
1413     if ((*i)->group() == tab)
1414       (*i)->set_group(NULL);
1415     if ((*i)->opener() == tab)
1416       (*i)->set_opener(NULL);
1417   }
1418 }