tab_strip_model_unittest.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "app/system_monitor.h"
6#include "base/file_path.h"
7#include "base/file_util.h"
8#include "base/path_service.h"
9#include "base/string_number_conversions.h"
10#include "base/scoped_ptr.h"
11#include "base/string_util.h"
12#include "base/stl_util-inl.h"
13#include "base/string_number_conversions.h"
14#include "base/string_util.h"
15#include "base/utf_string_conversions.h"
16#include "chrome/browser/defaults.h"
17#include "chrome/browser/dom_ui/new_tab_ui.h"
18#include "chrome/browser/profile.h"
19#include "chrome/browser/profile_manager.h"
20#include "chrome/browser/renderer_host/test/test_render_view_host.h"
21#include "chrome/browser/tab_contents/navigation_controller.h"
22#include "chrome/browser/tab_contents/navigation_entry.h"
23#include "chrome/browser/tab_contents/tab_contents.h"
24#include "chrome/browser/tab_contents_wrapper.h"
25#include "chrome/browser/tabs/tab_strip_model.h"
26#include "chrome/browser/tabs/tab_strip_model_delegate.h"
27#include "chrome/browser/tabs/tab_strip_model_order_controller.h"
28#include "chrome/browser/ui/browser.h"
29#include "chrome/common/extensions/extension.h"
30#include "chrome/common/notification_observer_mock.h"
31#include "chrome/common/notification_registrar.h"
32#include "chrome/common/notification_service.h"
33#include "chrome/common/pref_names.h"
34#include "chrome/common/property_bag.h"
35#include "chrome/common/url_constants.h"
36#include "chrome/test/testing_profile.h"
37#include "testing/gtest/include/gtest/gtest.h"
38
39using testing::_;
40
41namespace {
42
43// Class used to delete a TabContents when another TabContents is destroyed.
44class DeleteTabContentsOnDestroyedObserver : public NotificationObserver {
45 public:
46  DeleteTabContentsOnDestroyedObserver(TabContentsWrapper* source,
47                                       TabContentsWrapper* tab_to_delete)
48      : source_(source),
49        tab_to_delete_(tab_to_delete) {
50    registrar_.Add(this,
51                   NotificationType::TAB_CONTENTS_DESTROYED,
52                   Source<TabContents>(source->tab_contents()));
53  }
54
55  virtual void Observe(NotificationType type,
56                       const NotificationSource& source,
57                       const NotificationDetails& details) {
58    TabContentsWrapper* tab_to_delete = tab_to_delete_;
59    tab_to_delete_ = NULL;
60    delete tab_to_delete;
61  }
62
63 private:
64  TabContentsWrapper* source_;
65  TabContentsWrapper* tab_to_delete_;
66  NotificationRegistrar registrar_;
67
68  DISALLOW_COPY_AND_ASSIGN(DeleteTabContentsOnDestroyedObserver);
69};
70
71}  // namespace
72
73class TabStripDummyDelegate : public TabStripModelDelegate {
74 public:
75  explicit TabStripDummyDelegate(TabContentsWrapper* dummy)
76      : dummy_contents_(dummy), can_close_(true), run_unload_(false) {}
77  virtual ~TabStripDummyDelegate() {}
78
79  void set_can_close(bool value) { can_close_ = value; }
80  void set_run_unload_listener(bool value) { run_unload_ = value; }
81
82  // Overridden from TabStripModelDelegate:
83  virtual TabContentsWrapper* AddBlankTab(bool foreground) {
84    return NULL;
85  }
86  virtual TabContentsWrapper* AddBlankTabAt(int index, bool foreground) {
87    return NULL;
88  }
89  virtual Browser* CreateNewStripWithContents(TabContentsWrapper* contents,
90                                              const gfx::Rect& window_bounds,
91                                              const DockInfo& dock_info,
92                                              bool maximize) {
93    return NULL;
94  }
95  virtual void ContinueDraggingDetachedTab(TabContentsWrapper* contents,
96                                           const gfx::Rect& window_bounds,
97                                           const gfx::Rect& tab_bounds) {
98  }
99  virtual int GetDragActions() const { return 0; }
100  virtual TabContentsWrapper* CreateTabContentsForURL(
101      const GURL& url,
102      const GURL& referrer,
103      Profile* profile,
104      PageTransition::Type transition,
105      bool defer_load,
106      SiteInstance* instance) const {
107    if (url == GURL(chrome::kChromeUINewTabURL))
108      return dummy_contents_;
109    return NULL;
110  }
111  virtual bool CanDuplicateContentsAt(int index) { return false; }
112  virtual void DuplicateContentsAt(int index) {}
113  virtual void CloseFrameAfterDragSession() {}
114  virtual void CreateHistoricalTab(TabContentsWrapper* contents) {}
115  virtual bool RunUnloadListenerBeforeClosing(TabContentsWrapper* contents) {
116    return run_unload_;
117  }
118  virtual bool CanRestoreTab() { return false; }
119  virtual void RestoreTab() {}
120  virtual bool CanCloseContentsAt(int index) { return can_close_ ; }
121  virtual bool CanBookmarkAllTabs() const { return false; }
122  virtual void BookmarkAllTabs() {}
123  virtual bool CanCloseTab() const { return true; }
124  virtual bool UseVerticalTabs() const { return false; }
125  virtual void ToggleUseVerticalTabs() {}
126  virtual bool LargeIconsPermitted() const { return true; }
127
128 private:
129  // A dummy TabContents we give to callers that expect us to actually build a
130  // Destinations tab for them.
131  TabContentsWrapper* dummy_contents_;
132
133  // Whether tabs can be closed.
134  bool can_close_;
135
136  // Whether to report that we need to run an unload listener before closing.
137  bool run_unload_;
138
139  DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
140};
141
142class TabStripModelTest : public RenderViewHostTestHarness {
143 public:
144  TabContentsWrapper* CreateTabContents() {
145    return Browser::TabContentsFactory(profile(), NULL, 0, NULL, NULL);
146  }
147
148  TabContentsWrapper* CreateTabContentsWithSharedRPH(
149      TabContents* tab_contents) {
150    TabContentsWrapper* retval = Browser::TabContentsFactory(profile(),
151        tab_contents->render_view_host()->site_instance(), MSG_ROUTING_NONE,
152        NULL, NULL);
153    EXPECT_EQ(retval->tab_contents()->GetRenderProcessHost(),
154              tab_contents->GetRenderProcessHost());
155    return retval;
156  }
157
158  // Forwards a URL "load" request through to our dummy TabContents
159  // implementation.
160  void LoadURL(TabContents* con, const std::wstring& url) {
161    controller().LoadURL(GURL(WideToUTF16(url)), GURL(), PageTransition::LINK);
162  }
163
164  void GoBack(TabContents* contents) {
165    controller().GoBack();
166  }
167
168  void GoForward(TabContents* contents) {
169    controller().GoForward();
170  }
171
172  void SwitchTabTo(TabContents* contents) {
173    // contents()->DidBecomeSelected();
174  }
175
176  // Sets the id of the specified contents.
177  void SetID(TabContents* contents, int id) {
178    GetIDAccessor()->SetProperty(contents->property_bag(), id);
179  }
180
181  // Returns the id of the specified contents.
182  int GetID(TabContents* contents) {
183    return *GetIDAccessor()->GetProperty(contents->property_bag());
184  }
185
186  // Returns the state of the given tab strip as a string. The state consists
187  // of the ID of each tab contents followed by a 'p' if pinned. For example,
188  // if the model consists of two tabs with ids 2 and 1, with the first
189  // tab pinned, this returns "2p 1".
190  std::string GetPinnedState(const TabStripModel& model) {
191    std::string actual;
192    for (int i = 0; i < model.count(); ++i) {
193      if (i > 0)
194        actual += " ";
195
196      actual +=
197          base::IntToString(GetID(model.GetTabContentsAt(i)->tab_contents()));
198
199      if (model.IsAppTab(i))
200        actual += "a";
201
202      if (model.IsTabPinned(i))
203        actual += "p";
204    }
205    return actual;
206  }
207
208  std::string GetIndicesClosedByCommandAsString(
209      const TabStripModel& model,
210      int index,
211      TabStripModel::ContextMenuCommand id) const {
212    std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
213    std::string result;
214    for (size_t i = 0; i < indices.size(); ++i) {
215      if (i != 0)
216        result += " ";
217      result += base::IntToString(indices[i]);
218    }
219    return result;
220  }
221
222 private:
223  PropertyAccessor<int>* GetIDAccessor() {
224    static PropertyAccessor<int> accessor;
225    return &accessor;
226  }
227
228  std::wstring test_dir_;
229  std::wstring profile_path_;
230  std::map<TabContents*, int> foo_;
231
232  // ProfileManager requires a SystemMonitor.
233  SystemMonitor system_monitor;
234
235  ProfileManager pm_;
236};
237
238class MockTabStripModelObserver : public TabStripModelObserver {
239 public:
240  MockTabStripModelObserver() : empty_(true) {}
241  ~MockTabStripModelObserver() {
242    STLDeleteContainerPointers(states_.begin(), states_.end());
243  }
244
245  enum TabStripModelObserverAction {
246    INSERT,
247    CLOSE,
248    DETACH,
249    SELECT,
250    MOVE,
251    CHANGE,
252    PINNED,
253    REPLACED
254  };
255
256  struct State {
257    State(TabContentsWrapper* a_dst_contents,
258          int a_dst_index,
259          TabStripModelObserverAction a_action)
260        : src_contents(NULL),
261          dst_contents(a_dst_contents),
262          src_index(-1),
263          dst_index(a_dst_index),
264          user_gesture(false),
265          foreground(false),
266          action(a_action) {
267    }
268
269    TabContentsWrapper* src_contents;
270    TabContentsWrapper* dst_contents;
271    int src_index;
272    int dst_index;
273    bool user_gesture;
274    bool foreground;
275    TabStripModelObserverAction action;
276  };
277
278  int GetStateCount() const {
279    return static_cast<int>(states_.size());
280  }
281
282  State* GetStateAt(int index) const {
283    DCHECK(index >= 0 && index < GetStateCount());
284    return states_.at(index);
285  }
286
287  bool StateEquals(int index, const State& state) {
288    State* s = GetStateAt(index);
289    EXPECT_EQ(state.src_contents, s->src_contents);
290    EXPECT_EQ(state.dst_contents, s->dst_contents);
291    EXPECT_EQ(state.src_index, s->src_index);
292    EXPECT_EQ(state.dst_index, s->dst_index);
293    EXPECT_EQ(state.user_gesture, s->user_gesture);
294    EXPECT_EQ(state.foreground, s->foreground);
295    EXPECT_EQ(state.action, s->action);
296    return (s->src_contents == state.src_contents &&
297            s->dst_contents == state.dst_contents &&
298            s->src_index == state.src_index &&
299            s->dst_index == state.dst_index &&
300            s->user_gesture == state.user_gesture &&
301            s->foreground == state.foreground &&
302            s->action == state.action);
303  }
304
305  // TabStripModelObserver implementation:
306  virtual void TabInsertedAt(TabContentsWrapper* contents,
307                             int index,
308                             bool foreground) {
309    empty_ = false;
310    State* s = new State(contents, index, INSERT);
311    s->foreground = foreground;
312    states_.push_back(s);
313  }
314  virtual void TabSelectedAt(TabContentsWrapper* old_contents,
315                             TabContentsWrapper* new_contents,
316                             int index,
317                             bool user_gesture) {
318    State* s = new State(new_contents, index, SELECT);
319    s->src_contents = old_contents;
320    s->user_gesture = user_gesture;
321    states_.push_back(s);
322  }
323  virtual void TabMoved(
324      TabContentsWrapper* contents, int from_index, int to_index) {
325    State* s = new State(contents, to_index, MOVE);
326    s->src_index = from_index;
327    states_.push_back(s);
328  }
329
330  virtual void TabClosingAt(TabStripModel* tab_strip_model,
331                            TabContentsWrapper* contents,
332                            int index) {
333    states_.push_back(new State(contents, index, CLOSE));
334  }
335  virtual void TabDetachedAt(TabContentsWrapper* contents, int index) {
336    states_.push_back(new State(contents, index, DETACH));
337  }
338  virtual void TabChangedAt(TabContentsWrapper* contents, int index,
339                            TabChangeType change_type) {
340    states_.push_back(new State(contents, index, CHANGE));
341  }
342  virtual void TabReplacedAt(TabContentsWrapper* old_contents,
343                             TabContentsWrapper* new_contents, int index) {
344    State* s = new State(new_contents, index, REPLACED);
345    s ->src_contents = old_contents;
346    states_.push_back(s);
347  }
348  virtual void TabPinnedStateChanged(TabContentsWrapper* contents, int index) {
349    states_.push_back(new State(contents, index, PINNED));
350  }
351  virtual void TabStripEmpty() {
352    empty_ = true;
353  }
354
355  void ClearStates() {
356    STLDeleteContainerPointers(states_.begin(), states_.end());
357    states_.clear();
358  }
359
360  bool empty() const { return empty_; }
361
362 private:
363  std::vector<State*> states_;
364
365  bool empty_;
366
367  DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
368};
369
370TEST_F(TabStripModelTest, TestBasicAPI) {
371  TabStripDummyDelegate delegate(NULL);
372  TabStripModel tabstrip(&delegate, profile());
373  MockTabStripModelObserver observer;
374  tabstrip.AddObserver(&observer);
375
376  EXPECT_TRUE(tabstrip.empty());
377
378  typedef MockTabStripModelObserver::State State;
379
380  TabContentsWrapper* contents1 = CreateTabContents();
381
382  // Note! The ordering of these tests is important, each subsequent test
383  // builds on the state established in the previous. This is important if you
384  // ever insert tests rather than append.
385
386  // Test AppendTabContents, ContainsIndex
387  {
388    EXPECT_FALSE(tabstrip.ContainsIndex(0));
389    tabstrip.AppendTabContents(contents1, true);
390    EXPECT_TRUE(tabstrip.ContainsIndex(0));
391    EXPECT_EQ(1, tabstrip.count());
392    EXPECT_EQ(2, observer.GetStateCount());
393    State s1(contents1, 0, MockTabStripModelObserver::INSERT);
394    s1.foreground = true;
395    EXPECT_TRUE(observer.StateEquals(0, s1));
396    State s2(contents1, 0, MockTabStripModelObserver::SELECT);
397    s2.src_contents = NULL;
398    EXPECT_TRUE(observer.StateEquals(1, s2));
399    observer.ClearStates();
400  }
401
402  // Test InsertTabContentsAt, foreground tab.
403  TabContentsWrapper* contents2 = CreateTabContents();
404  {
405    tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_SELECTED);
406
407    EXPECT_EQ(2, tabstrip.count());
408    EXPECT_EQ(2, observer.GetStateCount());
409    State s1(contents2, 1, MockTabStripModelObserver::INSERT);
410    s1.foreground = true;
411    EXPECT_TRUE(observer.StateEquals(0, s1));
412    State s2(contents2, 1, MockTabStripModelObserver::SELECT);
413    s2.src_contents = contents1;
414    EXPECT_TRUE(observer.StateEquals(1, s2));
415    observer.ClearStates();
416  }
417
418  // Test InsertTabContentsAt, background tab.
419  TabContentsWrapper* contents3 = CreateTabContents();
420  {
421    tabstrip.InsertTabContentsAt(2, contents3, TabStripModel::ADD_NONE);
422
423    EXPECT_EQ(3, tabstrip.count());
424    EXPECT_EQ(1, observer.GetStateCount());
425    State s1(contents3, 2, MockTabStripModelObserver::INSERT);
426    s1.foreground = false;
427    EXPECT_TRUE(observer.StateEquals(0, s1));
428    observer.ClearStates();
429  }
430
431  // Test SelectTabContentsAt
432  {
433    tabstrip.SelectTabContentsAt(2, true);
434    EXPECT_EQ(1, observer.GetStateCount());
435    State s1(contents3, 2, MockTabStripModelObserver::SELECT);
436    s1.src_contents = contents2;
437    s1.user_gesture = true;
438    EXPECT_TRUE(observer.StateEquals(0, s1));
439    observer.ClearStates();
440  }
441
442  // Test DetachTabContentsAt
443  {
444    // Detach
445    TabContentsWrapper* detached = tabstrip.DetachTabContentsAt(2);
446    // ... and append again because we want this for later.
447    tabstrip.AppendTabContents(detached, true);
448    EXPECT_EQ(4, observer.GetStateCount());
449    State s1(detached, 2, MockTabStripModelObserver::DETACH);
450    EXPECT_TRUE(observer.StateEquals(0, s1));
451    State s2(contents2, 1, MockTabStripModelObserver::SELECT);
452    s2.src_contents = contents3;
453    s2.user_gesture = false;
454    EXPECT_TRUE(observer.StateEquals(1, s2));
455    State s3(detached, 2, MockTabStripModelObserver::INSERT);
456    s3.foreground = true;
457    EXPECT_TRUE(observer.StateEquals(2, s3));
458    State s4(detached, 2, MockTabStripModelObserver::SELECT);
459    s4.src_contents = contents2;
460    s4.user_gesture = false;
461    EXPECT_TRUE(observer.StateEquals(3, s4));
462    observer.ClearStates();
463  }
464
465  // Test CloseTabContentsAt
466  {
467    // Let's test nothing happens when the delegate veto the close.
468    delegate.set_can_close(false);
469    EXPECT_FALSE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE));
470    EXPECT_EQ(3, tabstrip.count());
471    EXPECT_EQ(0, observer.GetStateCount());
472
473    // Now let's close for real.
474    delegate.set_can_close(true);
475    EXPECT_TRUE(tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE));
476    EXPECT_EQ(2, tabstrip.count());
477
478    EXPECT_EQ(3, observer.GetStateCount());
479    State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
480    EXPECT_TRUE(observer.StateEquals(0, s1));
481    State s2(contents3, 2, MockTabStripModelObserver::DETACH);
482    EXPECT_TRUE(observer.StateEquals(1, s2));
483    State s3(contents2, 1, MockTabStripModelObserver::SELECT);
484    s3.src_contents = contents3;
485    s3.user_gesture = false;
486    EXPECT_TRUE(observer.StateEquals(2, s3));
487    observer.ClearStates();
488  }
489
490  // Test MoveTabContentsAt, select_after_move == true
491  {
492    tabstrip.MoveTabContentsAt(1, 0, true);
493
494    EXPECT_EQ(1, observer.GetStateCount());
495    State s1(contents2, 0, MockTabStripModelObserver::MOVE);
496    s1.src_index = 1;
497    EXPECT_TRUE(observer.StateEquals(0, s1));
498    EXPECT_EQ(0, tabstrip.selected_index());
499    observer.ClearStates();
500  }
501
502  // Test MoveTabContentsAt, select_after_move == false
503  {
504    tabstrip.MoveTabContentsAt(1, 0, false);
505    EXPECT_EQ(1, observer.GetStateCount());
506    State s1(contents1, 0, MockTabStripModelObserver::MOVE);
507    s1.src_index = 1;
508    EXPECT_TRUE(observer.StateEquals(0, s1));
509    EXPECT_EQ(1, tabstrip.selected_index());
510
511    tabstrip.MoveTabContentsAt(0, 1, false);
512    observer.ClearStates();
513  }
514
515  // Test Getters
516  {
517    EXPECT_EQ(contents2, tabstrip.GetSelectedTabContents());
518    EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(0));
519    EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
520    EXPECT_EQ(0, tabstrip.GetIndexOfTabContents(contents2));
521    EXPECT_EQ(1, tabstrip.GetIndexOfTabContents(contents1));
522    EXPECT_EQ(0, tabstrip.GetIndexOfController(&contents2->controller()));
523    EXPECT_EQ(1, tabstrip.GetIndexOfController(&contents1->controller()));
524  }
525
526  // Test UpdateTabContentsStateAt
527  {
528    tabstrip.UpdateTabContentsStateAt(0, TabStripModelObserver::ALL);
529    EXPECT_EQ(1, observer.GetStateCount());
530    State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
531    EXPECT_TRUE(observer.StateEquals(0, s1));
532    observer.ClearStates();
533  }
534
535  // Test SelectNextTab, SelectPreviousTab, SelectLastTab
536  {
537    // Make sure the second of the two tabs is selected first...
538    tabstrip.SelectTabContentsAt(1, true);
539    tabstrip.SelectPreviousTab();
540    EXPECT_EQ(0, tabstrip.selected_index());
541    tabstrip.SelectLastTab();
542    EXPECT_EQ(1, tabstrip.selected_index());
543    tabstrip.SelectNextTab();
544    EXPECT_EQ(0, tabstrip.selected_index());
545  }
546
547  // Test CloseSelectedTab
548  {
549    tabstrip.CloseSelectedTab();
550    // |CloseSelectedTab| calls CloseTabContentsAt, we already tested that, now
551    // just verify that the count and selected index have changed
552    // appropriately...
553    EXPECT_EQ(1, tabstrip.count());
554    EXPECT_EQ(0, tabstrip.selected_index());
555  }
556
557  tabstrip.CloseAllTabs();
558  // TabStripModel should now be empty.
559  EXPECT_TRUE(tabstrip.empty());
560
561  // Opener methods are tested below...
562
563  tabstrip.RemoveObserver(&observer);
564}
565
566TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
567  TabStripDummyDelegate delegate(NULL);
568  TabStripModel tabstrip(&delegate, profile());
569  EXPECT_TRUE(tabstrip.empty());
570
571  // This is a basic test of opener functionality. opener_contents is created
572  // as the first tab in the strip and then we create 5 other tabs in the
573  // background with opener_contents set as their opener.
574
575  TabContentsWrapper* opener_contents = CreateTabContents();
576  NavigationController* opener = &opener_contents->controller();
577  tabstrip.AppendTabContents(opener_contents, true);
578  TabContentsWrapper* contents1 = CreateTabContents();
579  TabContentsWrapper* contents2 = CreateTabContents();
580  TabContentsWrapper* contents3 = CreateTabContents();
581  TabContentsWrapper* contents4 = CreateTabContents();
582  TabContentsWrapper* contents5 = CreateTabContents();
583
584  // We use |InsertTabContentsAt| here instead of AppendTabContents so that
585  // openership relationships are preserved.
586  tabstrip.InsertTabContentsAt(tabstrip.count(), contents1,
587                               TabStripModel::ADD_INHERIT_GROUP);
588  tabstrip.InsertTabContentsAt(tabstrip.count(), contents2,
589                               TabStripModel::ADD_INHERIT_GROUP);
590  tabstrip.InsertTabContentsAt(tabstrip.count(), contents3,
591                               TabStripModel::ADD_INHERIT_GROUP);
592  tabstrip.InsertTabContentsAt(tabstrip.count(), contents4,
593                               TabStripModel::ADD_INHERIT_GROUP);
594  tabstrip.InsertTabContentsAt(tabstrip.count(), contents5,
595                               TabStripModel::ADD_INHERIT_GROUP);
596
597  // All the tabs should have the same opener.
598  for (int i = 1; i < tabstrip.count(); ++i)
599    EXPECT_EQ(opener, tabstrip.GetOpenerOfTabContentsAt(i));
600
601  // If there is a next adjacent item, then the index should be of that item.
602  EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false));
603  // If the last tab in the group is closed, the preceding tab in the same
604  // group should be selected.
605  EXPECT_EQ(4, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false));
606
607  // Tests the method that finds the last tab opened by the same opener in the
608  // strip (this is the insertion index for the next background tab for the
609  // specified opener).
610  EXPECT_EQ(5, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
611
612  // For a tab that has opened no other tabs, the return value should always be
613  // -1...
614  NavigationController* o1 = &contents1->controller();
615  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(o1, 3, false));
616  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(o1, 3));
617
618  // ForgetAllOpeners should destroy all opener relationships.
619  tabstrip.ForgetAllOpeners();
620  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 1, false));
621  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 5, false));
622  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
623
624  tabstrip.CloseAllTabs();
625  EXPECT_TRUE(tabstrip.empty());
626}
627
628static int GetInsertionIndex(TabStripModel* tabstrip,
629                             TabContentsWrapper* contents) {
630  return tabstrip->order_controller()->DetermineInsertionIndex(
631      contents, PageTransition::LINK, false);
632}
633
634static void InsertTabContentses(TabStripModel* tabstrip,
635                                TabContentsWrapper* contents1,
636                                TabContentsWrapper* contents2,
637                                TabContentsWrapper* contents3) {
638  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents1),
639                                contents1, TabStripModel::ADD_INHERIT_GROUP);
640  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents2),
641                                contents2, TabStripModel::ADD_INHERIT_GROUP);
642  tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents3),
643                                contents3, TabStripModel::ADD_INHERIT_GROUP);
644}
645
646// Tests opening background tabs.
647TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
648  TabStripDummyDelegate delegate(NULL);
649  TabStripModel tabstrip(&delegate, profile());
650  EXPECT_TRUE(tabstrip.empty());
651
652  TabContentsWrapper* opener_contents = CreateTabContents();
653  tabstrip.AppendTabContents(opener_contents, true);
654
655  TabContentsWrapper* contents1 = CreateTabContents();
656  TabContentsWrapper* contents2 = CreateTabContents();
657  TabContentsWrapper* contents3 = CreateTabContents();
658
659  // Test LTR
660  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
661  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
662  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2));
663  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3));
664
665  tabstrip.CloseAllTabs();
666  EXPECT_TRUE(tabstrip.empty());
667}
668
669// Tests inserting tabs with InsertAfter set to false.
670TEST_F(TabStripModelTest, InsertBefore) {
671  TabStripDummyDelegate delegate(NULL);
672  TabStripModel tabstrip(&delegate, profile());
673  tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE);
674  EXPECT_TRUE(tabstrip.empty());
675
676  TabContentsWrapper* contents1 = CreateTabContents();
677  TabContentsWrapper* contents2 = CreateTabContents();
678  TabContentsWrapper* contents3 = CreateTabContents();
679
680  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
681
682  // The order should be reversed.
683  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0));
684  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1));
685  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2));
686
687  tabstrip.CloseAllTabs();
688  EXPECT_TRUE(tabstrip.empty());
689}
690
691// Tests opening background tabs with InsertAfter set to false.
692TEST_F(TabStripModelTest, InsertBeforeOpeners) {
693  TabStripDummyDelegate delegate(NULL);
694  TabStripModel tabstrip(&delegate, profile());
695  tabstrip.SetInsertionPolicy(TabStripModel::INSERT_BEFORE);
696  EXPECT_TRUE(tabstrip.empty());
697  TabContentsWrapper* opener_contents = CreateTabContents();
698  tabstrip.AppendTabContents(opener_contents, true);
699
700  TabContentsWrapper* contents1 = CreateTabContents();
701  TabContentsWrapper* contents2 = CreateTabContents();
702  TabContentsWrapper* contents3 = CreateTabContents();
703
704  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
705
706  // The order should be reversed.
707  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(0));
708  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(1));
709  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(2));
710
711  tabstrip.CloseAllTabs();
712  EXPECT_TRUE(tabstrip.empty());
713}
714
715// This test constructs a tabstrip, and then simulates loading several tabs in
716// the background from link clicks on the first tab. Then it simulates opening
717// a new tab from the first tab in the foreground via a link click, verifies
718// that this tab is opened adjacent to the opener, then closes it.
719// Finally it tests that a tab opened for some non-link purpose openes at the
720// end of the strip, not bundled to any existing context.
721TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
722  TabStripDummyDelegate delegate(NULL);
723  TabStripModel tabstrip(&delegate, profile());
724  EXPECT_TRUE(tabstrip.empty());
725
726  TabContentsWrapper* opener_contents = CreateTabContents();
727  NavigationController* opener = &opener_contents->controller();
728  tabstrip.AppendTabContents(opener_contents, true);
729
730  // Open some other random unrelated tab in the background to monkey with our
731  // insertion index.
732  TabContentsWrapper* other_contents = CreateTabContents();
733  tabstrip.AppendTabContents(other_contents, false);
734
735  TabContentsWrapper* contents1 = CreateTabContents();
736  TabContentsWrapper* contents2 = CreateTabContents();
737  TabContentsWrapper* contents3 = CreateTabContents();
738
739  // Start by testing LTR
740  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
741  EXPECT_EQ(opener_contents, tabstrip.GetTabContentsAt(0));
742  EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1));
743  EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2));
744  EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3));
745  EXPECT_EQ(other_contents, tabstrip.GetTabContentsAt(4));
746
747  // The opener API should work...
748  EXPECT_EQ(3, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false));
749  EXPECT_EQ(2, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
750  EXPECT_EQ(3, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
751
752  // Now open a foreground tab from a link. It should be opened adjacent to the
753  // opener tab.
754  TabContentsWrapper* fg_link_contents = CreateTabContents();
755  int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
756      fg_link_contents, PageTransition::LINK, true);
757  EXPECT_EQ(1, insert_index);
758  tabstrip.InsertTabContentsAt(insert_index, fg_link_contents,
759                               TabStripModel::ADD_SELECTED |
760                               TabStripModel::ADD_INHERIT_GROUP);
761  EXPECT_EQ(1, tabstrip.selected_index());
762  EXPECT_EQ(fg_link_contents, tabstrip.GetSelectedTabContents());
763
764  // Now close this contents. The selection should move to the opener contents.
765  tabstrip.CloseSelectedTab();
766  EXPECT_EQ(0, tabstrip.selected_index());
767
768  // Now open a new empty tab. It should open at the end of the strip.
769  TabContentsWrapper* fg_nonlink_contents = CreateTabContents();
770  insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
771      fg_nonlink_contents, PageTransition::AUTO_BOOKMARK, true);
772  EXPECT_EQ(tabstrip.count(), insert_index);
773  // We break the opener relationship...
774  tabstrip.InsertTabContentsAt(insert_index, fg_nonlink_contents,
775                               TabStripModel::ADD_NONE);
776  // Now select it, so that user_gesture == true causes the opener relationship
777  // to be forgotten...
778  tabstrip.SelectTabContentsAt(tabstrip.count() - 1, true);
779  EXPECT_EQ(tabstrip.count() - 1, tabstrip.selected_index());
780  EXPECT_EQ(fg_nonlink_contents, tabstrip.GetSelectedTabContents());
781
782  // Verify that all opener relationships are forgotten.
783  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 2, false));
784  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
785  EXPECT_EQ(-1, tabstrip.GetIndexOfNextTabContentsOpenedBy(opener, 3, false));
786  EXPECT_EQ(-1, tabstrip.GetIndexOfLastTabContentsOpenedBy(opener, 1));
787
788  tabstrip.CloseAllTabs();
789  EXPECT_TRUE(tabstrip.empty());
790}
791
792// Tests that selection is shifted to the correct tab when a tab is closed.
793// If a tab is in the background when it is closed, the selection does not
794// change.
795// If a tab is in the foreground (selected),
796//   If that tab does not have an opener, selection shifts to the right.
797//   If the tab has an opener,
798//     The next tab (scanning LTR) in the entire strip that has the same opener
799//     is selected
800//     If there are no other tabs that have the same opener,
801//       The opener is selected
802//
803TEST_F(TabStripModelTest, TestSelectOnClose) {
804  TabStripDummyDelegate delegate(NULL);
805  TabStripModel tabstrip(&delegate, profile());
806  EXPECT_TRUE(tabstrip.empty());
807
808  TabContentsWrapper* opener_contents = CreateTabContents();
809  tabstrip.AppendTabContents(opener_contents, true);
810
811  TabContentsWrapper* contents1 = CreateTabContents();
812  TabContentsWrapper* contents2 = CreateTabContents();
813  TabContentsWrapper* contents3 = CreateTabContents();
814
815  // Note that we use Detach instead of Close throughout this test to avoid
816  // having to keep reconstructing these TabContentses.
817
818  // First test that closing tabs that are in the background doesn't adjust the
819  // current selection.
820  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
821  EXPECT_EQ(0, tabstrip.selected_index());
822
823  tabstrip.DetachTabContentsAt(1);
824  EXPECT_EQ(0, tabstrip.selected_index());
825
826  for (int i = tabstrip.count() - 1; i >= 1; --i)
827    tabstrip.DetachTabContentsAt(i);
828
829  // Now test that when a tab doesn't have an opener, selection shifts to the
830  // right when the tab is closed.
831  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
832  EXPECT_EQ(0, tabstrip.selected_index());
833
834  tabstrip.ForgetAllOpeners();
835  tabstrip.SelectTabContentsAt(1, true);
836  EXPECT_EQ(1, tabstrip.selected_index());
837  tabstrip.DetachTabContentsAt(1);
838  EXPECT_EQ(1, tabstrip.selected_index());
839  tabstrip.DetachTabContentsAt(1);
840  EXPECT_EQ(1, tabstrip.selected_index());
841  tabstrip.DetachTabContentsAt(1);
842  EXPECT_EQ(0, tabstrip.selected_index());
843
844  for (int i = tabstrip.count() - 1; i >= 1; --i)
845    tabstrip.DetachTabContentsAt(i);
846
847  // Now test that when a tab does have an opener, it selects the next tab
848  // opened by the same opener scanning LTR when it is closed.
849  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
850  EXPECT_EQ(0, tabstrip.selected_index());
851  tabstrip.SelectTabContentsAt(2, false);
852  EXPECT_EQ(2, tabstrip.selected_index());
853  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
854  EXPECT_EQ(2, tabstrip.selected_index());
855  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
856  EXPECT_EQ(1, tabstrip.selected_index());
857  tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE);
858  EXPECT_EQ(0, tabstrip.selected_index());
859  // Finally test that when a tab has no "siblings" that the opener is
860  // selected.
861  TabContentsWrapper* other_contents = CreateTabContents();
862  tabstrip.InsertTabContentsAt(1, other_contents, TabStripModel::ADD_NONE);
863  EXPECT_EQ(2, tabstrip.count());
864  TabContentsWrapper* opened_contents = CreateTabContents();
865  tabstrip.InsertTabContentsAt(2, opened_contents,
866                               TabStripModel::ADD_SELECTED |
867                               TabStripModel::ADD_INHERIT_GROUP);
868  EXPECT_EQ(2, tabstrip.selected_index());
869  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
870  EXPECT_EQ(0, tabstrip.selected_index());
871
872  tabstrip.CloseAllTabs();
873  EXPECT_TRUE(tabstrip.empty());
874}
875
876// Tests the following context menu commands:
877//  - Close Tab
878//  - Close Other Tabs
879//  - Close Tabs To Right
880TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
881  TabStripDummyDelegate delegate(NULL);
882  TabStripModel tabstrip(&delegate, profile());
883  EXPECT_TRUE(tabstrip.empty());
884
885  TabContentsWrapper* opener_contents = CreateTabContents();
886  tabstrip.AppendTabContents(opener_contents, true);
887
888  TabContentsWrapper* contents1 = CreateTabContents();
889  TabContentsWrapper* contents2 = CreateTabContents();
890  TabContentsWrapper* contents3 = CreateTabContents();
891
892  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
893  EXPECT_EQ(0, tabstrip.selected_index());
894
895  tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
896  EXPECT_EQ(3, tabstrip.count());
897
898  tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
899  EXPECT_EQ(1, tabstrip.count());
900  EXPECT_EQ(opener_contents, tabstrip.GetSelectedTabContents());
901
902  TabContentsWrapper* dummy_contents = CreateTabContents();
903  tabstrip.AppendTabContents(dummy_contents, false);
904
905  contents1 = CreateTabContents();
906  contents2 = CreateTabContents();
907  contents3 = CreateTabContents();
908  InsertTabContentses(&tabstrip, contents1, contents2, contents3);
909  EXPECT_EQ(5, tabstrip.count());
910
911  int dummy_index = tabstrip.count() - 1;
912  tabstrip.SelectTabContentsAt(dummy_index, true);
913  EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents());
914
915  tabstrip.ExecuteContextMenuCommand(dummy_index,
916                                     TabStripModel::CommandCloseOtherTabs);
917  EXPECT_EQ(1, tabstrip.count());
918  EXPECT_EQ(dummy_contents, tabstrip.GetSelectedTabContents());
919
920  tabstrip.CloseAllTabs();
921  EXPECT_TRUE(tabstrip.empty());
922}
923
924// Tests GetIndicesClosedByCommand.
925TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
926  TabStripDummyDelegate delegate(NULL);
927  TabStripModel tabstrip(&delegate, profile());
928  EXPECT_TRUE(tabstrip.empty());
929
930  TabContentsWrapper* contents1 = CreateTabContents();
931  TabContentsWrapper* contents2 = CreateTabContents();
932  TabContentsWrapper* contents3 = CreateTabContents();
933  TabContentsWrapper* contents4 = CreateTabContents();
934  TabContentsWrapper* contents5 = CreateTabContents();
935
936  tabstrip.AppendTabContents(contents1, true);
937  tabstrip.AppendTabContents(contents2, true);
938  tabstrip.AppendTabContents(contents3, true);
939  tabstrip.AppendTabContents(contents4, true);
940  tabstrip.AppendTabContents(contents5, true);
941
942  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
943                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
944  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
945                tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
946
947  EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
948                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
949  EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
950                tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
951
952  // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
953  // commands.
954  tabstrip.SetTabPinned(0, true);
955  tabstrip.SetTabPinned(1, true);
956
957  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
958                tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
959  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
960                tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
961
962  EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
963                tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
964  EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
965                tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
966
967  tabstrip.CloseAllTabs();
968  EXPECT_TRUE(tabstrip.empty());
969}
970
971// Tests whether or not TabContentses are inserted in the correct position
972// using this "smart" function with a simulated middle click action on a series
973// of links on the home page.
974TEST_F(TabStripModelTest, AddTabContents_MiddleClickLinksAndClose) {
975  TabStripDummyDelegate delegate(NULL);
976  TabStripModel tabstrip(&delegate, profile());
977  EXPECT_TRUE(tabstrip.empty());
978
979  // Open the Home Page
980  TabContentsWrapper* homepage_contents = CreateTabContents();
981  tabstrip.AddTabContents(
982      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
983      TabStripModel::ADD_SELECTED);
984
985  // Open some other tab, by user typing.
986  TabContentsWrapper* typed_page_contents = CreateTabContents();
987  tabstrip.AddTabContents(
988      typed_page_contents, -1, PageTransition::TYPED,
989      TabStripModel::ADD_SELECTED);
990
991  EXPECT_EQ(2, tabstrip.count());
992
993  // Re-select the home page.
994  tabstrip.SelectTabContentsAt(0, true);
995
996  // Open a bunch of tabs by simulating middle clicking on links on the home
997  // page.
998  TabContentsWrapper* middle_click_contents1 = CreateTabContents();
999  tabstrip.AddTabContents(
1000      middle_click_contents1, -1, PageTransition::LINK,
1001      TabStripModel::ADD_NONE);
1002  TabContentsWrapper* middle_click_contents2 = CreateTabContents();
1003  tabstrip.AddTabContents(
1004      middle_click_contents2, -1, PageTransition::LINK,
1005      TabStripModel::ADD_NONE);
1006  TabContentsWrapper* middle_click_contents3 = CreateTabContents();
1007  tabstrip.AddTabContents(
1008      middle_click_contents3, -1, PageTransition::LINK,
1009      TabStripModel::ADD_NONE);
1010
1011  EXPECT_EQ(5, tabstrip.count());
1012
1013  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1014  EXPECT_EQ(middle_click_contents1, tabstrip.GetTabContentsAt(1));
1015  EXPECT_EQ(middle_click_contents2, tabstrip.GetTabContentsAt(2));
1016  EXPECT_EQ(middle_click_contents3, tabstrip.GetTabContentsAt(3));
1017  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4));
1018
1019  // Now simulate seleting a tab in the middle of the group of tabs opened from
1020  // the home page and start closing them. Each TabContents in the group should
1021  // be closed, right to left. This test is constructed to start at the middle
1022  // TabContents in the group to make sure the cursor wraps around to the first
1023  // TabContents in the group before closing the opener or any other
1024  // TabContents.
1025  tabstrip.SelectTabContentsAt(2, true);
1026  tabstrip.CloseSelectedTab();
1027  EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents());
1028  tabstrip.CloseSelectedTab();
1029  EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents());
1030  tabstrip.CloseSelectedTab();
1031  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1032  tabstrip.CloseSelectedTab();
1033  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1034
1035  EXPECT_EQ(1, tabstrip.count());
1036
1037  tabstrip.CloseAllTabs();
1038  EXPECT_TRUE(tabstrip.empty());
1039}
1040
1041// Tests whether or not a TabContents created by a left click on a link that
1042// opens a new tab is inserted correctly adjacent to the tab that spawned it.
1043TEST_F(TabStripModelTest, AddTabContents_LeftClickPopup) {
1044  TabStripDummyDelegate delegate(NULL);
1045  TabStripModel tabstrip(&delegate, profile());
1046  EXPECT_TRUE(tabstrip.empty());
1047
1048  // Open the Home Page
1049  TabContentsWrapper* homepage_contents = CreateTabContents();
1050  tabstrip.AddTabContents(
1051      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1052      TabStripModel::ADD_SELECTED);
1053
1054  // Open some other tab, by user typing.
1055  TabContentsWrapper* typed_page_contents = CreateTabContents();
1056  tabstrip.AddTabContents(
1057      typed_page_contents, -1, PageTransition::TYPED,
1058      TabStripModel::ADD_SELECTED);
1059
1060  EXPECT_EQ(2, tabstrip.count());
1061
1062  // Re-select the home page.
1063  tabstrip.SelectTabContentsAt(0, true);
1064
1065  // Open a tab by simulating a left click on a link that opens in a new tab.
1066  TabContentsWrapper* left_click_contents = CreateTabContents();
1067  tabstrip.AddTabContents(left_click_contents, -1, PageTransition::LINK,
1068                          TabStripModel::ADD_SELECTED);
1069
1070  // Verify the state meets our expectations.
1071  EXPECT_EQ(3, tabstrip.count());
1072  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1073  EXPECT_EQ(left_click_contents, tabstrip.GetTabContentsAt(1));
1074  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(2));
1075
1076  // The newly created tab should be selected.
1077  EXPECT_EQ(left_click_contents, tabstrip.GetSelectedTabContents());
1078
1079  // After closing the selected tab, the selection should move to the left, to
1080  // the opener.
1081  tabstrip.CloseSelectedTab();
1082  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1083
1084  EXPECT_EQ(2, tabstrip.count());
1085
1086  tabstrip.CloseAllTabs();
1087  EXPECT_TRUE(tabstrip.empty());
1088}
1089
1090// Tests whether or not new tabs that should split context (typed pages,
1091// generated urls, also blank tabs) open at the end of the tabstrip instead of
1092// in the middle.
1093TEST_F(TabStripModelTest, AddTabContents_CreateNewBlankTab) {
1094  TabStripDummyDelegate delegate(NULL);
1095  TabStripModel tabstrip(&delegate, profile());
1096  EXPECT_TRUE(tabstrip.empty());
1097
1098  // Open the Home Page
1099  TabContentsWrapper* homepage_contents = CreateTabContents();
1100  tabstrip.AddTabContents(
1101      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1102      TabStripModel::ADD_SELECTED);
1103
1104  // Open some other tab, by user typing.
1105  TabContentsWrapper* typed_page_contents = CreateTabContents();
1106  tabstrip.AddTabContents(
1107      typed_page_contents, -1, PageTransition::TYPED,
1108      TabStripModel::ADD_SELECTED);
1109
1110  EXPECT_EQ(2, tabstrip.count());
1111
1112  // Re-select the home page.
1113  tabstrip.SelectTabContentsAt(0, true);
1114
1115  // Open a new blank tab in the foreground.
1116  TabContentsWrapper* new_blank_contents = CreateTabContents();
1117  tabstrip.AddTabContents(new_blank_contents, -1, PageTransition::TYPED,
1118                          TabStripModel::ADD_SELECTED);
1119
1120  // Verify the state of the tabstrip.
1121  EXPECT_EQ(3, tabstrip.count());
1122  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1123  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1));
1124  EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2));
1125
1126  // Now open a couple more blank tabs in the background.
1127  TabContentsWrapper* background_blank_contents1 = CreateTabContents();
1128  tabstrip.AddTabContents(
1129      background_blank_contents1, -1, PageTransition::TYPED,
1130      TabStripModel::ADD_NONE);
1131  TabContentsWrapper* background_blank_contents2 = CreateTabContents();
1132  tabstrip.AddTabContents(
1133      background_blank_contents2, -1, PageTransition::GENERATED,
1134      TabStripModel::ADD_NONE);
1135  EXPECT_EQ(5, tabstrip.count());
1136  EXPECT_EQ(homepage_contents, tabstrip.GetTabContentsAt(0));
1137  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(1));
1138  EXPECT_EQ(new_blank_contents, tabstrip.GetTabContentsAt(2));
1139  EXPECT_EQ(background_blank_contents1, tabstrip.GetTabContentsAt(3));
1140  EXPECT_EQ(background_blank_contents2, tabstrip.GetTabContentsAt(4));
1141
1142  tabstrip.CloseAllTabs();
1143  EXPECT_TRUE(tabstrip.empty());
1144}
1145
1146// Tests whether opener state is correctly forgotten when the user switches
1147// context.
1148TEST_F(TabStripModelTest, AddTabContents_ForgetOpeners) {
1149  TabStripDummyDelegate delegate(NULL);
1150  TabStripModel tabstrip(&delegate, profile());
1151  EXPECT_TRUE(tabstrip.empty());
1152
1153  // Open the Home Page
1154  TabContentsWrapper* homepage_contents = CreateTabContents();
1155  tabstrip.AddTabContents(
1156      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1157      TabStripModel::ADD_SELECTED);
1158
1159  // Open some other tab, by user typing.
1160  TabContentsWrapper* typed_page_contents = CreateTabContents();
1161  tabstrip.AddTabContents(
1162      typed_page_contents, -1, PageTransition::TYPED,
1163      TabStripModel::ADD_SELECTED);
1164
1165  EXPECT_EQ(2, tabstrip.count());
1166
1167  // Re-select the home page.
1168  tabstrip.SelectTabContentsAt(0, true);
1169
1170  // Open a bunch of tabs by simulating middle clicking on links on the home
1171  // page.
1172  TabContentsWrapper* middle_click_contents1 = CreateTabContents();
1173  tabstrip.AddTabContents(
1174      middle_click_contents1, -1, PageTransition::LINK,
1175      TabStripModel::ADD_NONE);
1176  TabContentsWrapper* middle_click_contents2 = CreateTabContents();
1177  tabstrip.AddTabContents(
1178      middle_click_contents2, -1, PageTransition::LINK,
1179      TabStripModel::ADD_NONE);
1180  TabContentsWrapper* middle_click_contents3 = CreateTabContents();
1181  tabstrip.AddTabContents(
1182      middle_click_contents3, -1, PageTransition::LINK,
1183      TabStripModel::ADD_NONE);
1184
1185  // Break out of the context by selecting a tab in a different context.
1186  EXPECT_EQ(typed_page_contents, tabstrip.GetTabContentsAt(4));
1187  tabstrip.SelectLastTab();
1188  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1189
1190  // Step back into the context by selecting a tab inside it.
1191  tabstrip.SelectTabContentsAt(2, true);
1192  EXPECT_EQ(middle_click_contents2, tabstrip.GetSelectedTabContents());
1193
1194  // Now test that closing tabs selects to the right until there are no more,
1195  // then to the left, as if there were no context (context has been
1196  // successfully forgotten).
1197  tabstrip.CloseSelectedTab();
1198  EXPECT_EQ(middle_click_contents3, tabstrip.GetSelectedTabContents());
1199  tabstrip.CloseSelectedTab();
1200  EXPECT_EQ(typed_page_contents, tabstrip.GetSelectedTabContents());
1201  tabstrip.CloseSelectedTab();
1202  EXPECT_EQ(middle_click_contents1, tabstrip.GetSelectedTabContents());
1203  tabstrip.CloseSelectedTab();
1204  EXPECT_EQ(homepage_contents, tabstrip.GetSelectedTabContents());
1205
1206  EXPECT_EQ(1, tabstrip.count());
1207
1208  tabstrip.CloseAllTabs();
1209  EXPECT_TRUE(tabstrip.empty());
1210}
1211
1212// Added for http://b/issue?id=958960
1213TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1214  TabContents* fake_destinations_tab =
1215      new TabContents(profile(), NULL, 0, NULL, NULL);
1216  TabContentsWrapper wrapper(fake_destinations_tab);
1217  TabStripDummyDelegate delegate(&wrapper);
1218  TabStripModel tabstrip(&delegate, profile());
1219  EXPECT_TRUE(tabstrip.empty());
1220
1221  // Open the Home Page
1222  TabContentsWrapper* homepage_contents = CreateTabContents();
1223  tabstrip.AddTabContents(
1224      homepage_contents, -1, PageTransition::AUTO_BOOKMARK,
1225      TabStripModel::ADD_SELECTED);
1226
1227  // Open some other tab, by user typing.
1228  TabContentsWrapper* typed_page_contents = CreateTabContents();
1229  tabstrip.AddTabContents(
1230      typed_page_contents, -1, PageTransition::TYPED,
1231      TabStripModel::ADD_NONE);
1232
1233  // The selected tab should still be the first.
1234  EXPECT_EQ(0, tabstrip.selected_index());
1235
1236  // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1237  // and make sure the right tab gets selected when the new tab is closed.
1238  TabContentsWrapper* target_blank_contents = CreateTabContents();
1239  tabstrip.AppendTabContents(target_blank_contents, true);
1240  EXPECT_EQ(2, tabstrip.selected_index());
1241  tabstrip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
1242  EXPECT_EQ(0, tabstrip.selected_index());
1243
1244  // clean up after ourselves
1245  tabstrip.CloseAllTabs();
1246}
1247
1248// Added for http://b/issue?id=1027661
1249TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1250  TabStripDummyDelegate delegate(NULL);
1251  TabStripModel strip(&delegate, profile());
1252
1253  // Open page A
1254  TabContentsWrapper* page_a_contents = CreateTabContents();
1255  strip.AddTabContents(
1256      page_a_contents, -1, PageTransition::AUTO_BOOKMARK,
1257      TabStripModel::ADD_SELECTED);
1258
1259  // Simulate middle click to open page A.A and A.B
1260  TabContentsWrapper* page_a_a_contents = CreateTabContents();
1261  strip.AddTabContents(page_a_a_contents, -1, PageTransition::LINK,
1262                       TabStripModel::ADD_NONE);
1263  TabContentsWrapper* page_a_b_contents = CreateTabContents();
1264  strip.AddTabContents(page_a_b_contents, -1, PageTransition::LINK,
1265                       TabStripModel::ADD_NONE);
1266
1267  // Select page A.A
1268  strip.SelectTabContentsAt(1, true);
1269  EXPECT_EQ(page_a_a_contents, strip.GetSelectedTabContents());
1270
1271  // Simulate a middle click to open page A.A.A
1272  TabContentsWrapper* page_a_a_a_contents = CreateTabContents();
1273  strip.AddTabContents(page_a_a_a_contents, -1, PageTransition::LINK,
1274                       TabStripModel::ADD_NONE);
1275
1276  EXPECT_EQ(page_a_a_a_contents, strip.GetTabContentsAt(2));
1277
1278  // Close page A.A
1279  strip.CloseTabContentsAt(strip.selected_index(), TabStripModel::CLOSE_NONE);
1280
1281  // Page A.A.A should be selected, NOT A.B
1282  EXPECT_EQ(page_a_a_a_contents, strip.GetSelectedTabContents());
1283
1284  // Close page A.A.A
1285  strip.CloseTabContentsAt(strip.selected_index(), TabStripModel::CLOSE_NONE);
1286
1287  // Page A.B should be selected
1288  EXPECT_EQ(page_a_b_contents, strip.GetSelectedTabContents());
1289
1290  // Close page A.B
1291  strip.CloseTabContentsAt(strip.selected_index(), TabStripModel::CLOSE_NONE);
1292
1293  // Page A should be selected
1294  EXPECT_EQ(page_a_contents, strip.GetSelectedTabContents());
1295
1296  // Clean up.
1297  strip.CloseAllTabs();
1298}
1299
1300TEST_F(TabStripModelTest, AddTabContents_NewTabAtEndOfStripInheritsGroup) {
1301  TabStripDummyDelegate delegate(NULL);
1302  TabStripModel strip(&delegate, profile());
1303
1304  // Open page A
1305  TabContentsWrapper* page_a_contents = CreateTabContents();
1306  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1307                       TabStripModel::ADD_SELECTED);
1308
1309  // Open pages B, C and D in the background from links on page A...
1310  TabContentsWrapper* page_b_contents = CreateTabContents();
1311  TabContentsWrapper* page_c_contents = CreateTabContents();
1312  TabContentsWrapper* page_d_contents = CreateTabContents();
1313  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1314                       TabStripModel::ADD_NONE);
1315  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1316                       TabStripModel::ADD_NONE);
1317  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1318                       TabStripModel::ADD_NONE);
1319
1320  // Switch to page B's tab.
1321  strip.SelectTabContentsAt(1, true);
1322
1323  // Open a New Tab at the end of the strip (simulate Ctrl+T)
1324  TabContentsWrapper* new_tab_contents = CreateTabContents();
1325  strip.AddTabContents(new_tab_contents, -1, PageTransition::TYPED,
1326                       TabStripModel::ADD_SELECTED);
1327
1328  EXPECT_EQ(4, strip.GetIndexOfTabContents(new_tab_contents));
1329  EXPECT_EQ(4, strip.selected_index());
1330
1331  // Close the New Tab that was just opened. We should be returned to page B's
1332  // Tab...
1333  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1334
1335  EXPECT_EQ(1, strip.selected_index());
1336
1337  // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1338  // This is like typing a URL in the address bar and pressing Alt+Enter. The
1339  // behavior should be the same as above.
1340  TabContentsWrapper* page_e_contents = CreateTabContents();
1341  strip.AddTabContents(page_e_contents, -1, PageTransition::TYPED,
1342                       TabStripModel::ADD_SELECTED);
1343
1344  EXPECT_EQ(4, strip.GetIndexOfTabContents(page_e_contents));
1345  EXPECT_EQ(4, strip.selected_index());
1346
1347  // Close the Tab. Selection should shift back to page B's Tab.
1348  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1349
1350  EXPECT_EQ(1, strip.selected_index());
1351
1352  // Open a non-New Tab tab at the end of the strip, with some other
1353  // transition. This is like right clicking on a bookmark and choosing "Open
1354  // in New Tab". No opener relationship should be preserved between this Tab
1355  // and the one that was active when the gesture was performed.
1356  TabContentsWrapper* page_f_contents = CreateTabContents();
1357  strip.AddTabContents(page_f_contents, -1, PageTransition::AUTO_BOOKMARK,
1358                       TabStripModel::ADD_SELECTED);
1359
1360  EXPECT_EQ(4, strip.GetIndexOfTabContents(page_f_contents));
1361  EXPECT_EQ(4, strip.selected_index());
1362
1363  // Close the Tab. The next-adjacent should be selected.
1364  strip.CloseTabContentsAt(4, TabStripModel::CLOSE_NONE);
1365
1366  EXPECT_EQ(3, strip.selected_index());
1367
1368  // Clean up.
1369  strip.CloseAllTabs();
1370}
1371
1372// A test of navigations in a tab that is part of a group of opened from some
1373// parent tab. If the navigations are link clicks, the group relationship of
1374// the tab to its parent are preserved. If they are of any other type, they are
1375// not preserved.
1376TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1377  TabStripDummyDelegate delegate(NULL);
1378  TabStripModel strip(&delegate, profile());
1379
1380  // Open page A
1381  TabContentsWrapper* page_a_contents = CreateTabContents();
1382  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1383                       TabStripModel::ADD_SELECTED);
1384
1385  // Open pages B, C and D in the background from links on page A...
1386  TabContentsWrapper* page_b_contents = CreateTabContents();
1387  TabContentsWrapper* page_c_contents = CreateTabContents();
1388  TabContentsWrapper* page_d_contents = CreateTabContents();
1389  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1390                       TabStripModel::ADD_NONE);
1391  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1392                       TabStripModel::ADD_NONE);
1393  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1394                       TabStripModel::ADD_NONE);
1395
1396  // Open page E in a different opener group from page A.
1397  TabContentsWrapper* page_e_contents = CreateTabContents();
1398  strip.AddTabContents(page_e_contents, -1, PageTransition::START_PAGE,
1399                       TabStripModel::ADD_NONE);
1400
1401  // Tell the TabStripModel that we are navigating page D via a link click.
1402  strip.SelectTabContentsAt(3, true);
1403  strip.TabNavigating(page_d_contents, PageTransition::LINK);
1404
1405  // Close page D, page C should be selected. (part of same group).
1406  strip.CloseTabContentsAt(3, TabStripModel::CLOSE_NONE);
1407  EXPECT_EQ(2, strip.selected_index());
1408
1409  // Tell the TabStripModel that we are navigating in page C via a bookmark.
1410  strip.TabNavigating(page_c_contents, PageTransition::AUTO_BOOKMARK);
1411
1412  // Close page C, page E should be selected. (C is no longer part of the
1413  // A-B-C-D group, selection moves to the right).
1414  strip.CloseTabContentsAt(2, TabStripModel::CLOSE_NONE);
1415  EXPECT_EQ(page_e_contents, strip.GetTabContentsAt(strip.selected_index()));
1416
1417  strip.CloseAllTabs();
1418}
1419
1420// A test that the forgetting behavior tested in NavigationForgetsOpeners above
1421// doesn't cause the opener relationship for a New Tab opened at the end of the
1422// TabStrip to be reset (Test 1 below), unless another any other tab is
1423// seelcted (Test 2 below).
1424TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1425  TabStripDummyDelegate delegate(NULL);
1426  TabStripModel strip(&delegate, profile());
1427
1428  // Open a tab and several tabs from it, then select one of the tabs that was
1429  // opened.
1430  TabContentsWrapper* page_a_contents = CreateTabContents();
1431  strip.AddTabContents(page_a_contents, -1, PageTransition::START_PAGE,
1432                       TabStripModel::ADD_SELECTED);
1433
1434  TabContentsWrapper* page_b_contents = CreateTabContents();
1435  TabContentsWrapper* page_c_contents = CreateTabContents();
1436  TabContentsWrapper* page_d_contents = CreateTabContents();
1437  strip.AddTabContents(page_b_contents, -1, PageTransition::LINK,
1438                       TabStripModel::ADD_NONE);
1439  strip.AddTabContents(page_c_contents, -1, PageTransition::LINK,
1440                       TabStripModel::ADD_NONE);
1441  strip.AddTabContents(page_d_contents, -1, PageTransition::LINK,
1442                       TabStripModel::ADD_NONE);
1443
1444  strip.SelectTabContentsAt(2, true);
1445
1446  // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1447  // of the strip, closing that new tab will select the tab that they were
1448  // last on.
1449
1450  // Now simulate opening a new tab at the end of the TabStrip.
1451  TabContentsWrapper* new_tab_contents1 = CreateTabContents();
1452  strip.AddTabContents(new_tab_contents1, -1, PageTransition::TYPED,
1453                       TabStripModel::ADD_SELECTED);
1454
1455  // At this point, if we close this tab the last selected one should be
1456  // re-selected.
1457  strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1458  EXPECT_EQ(page_c_contents, strip.GetTabContentsAt(strip.selected_index()));
1459
1460  // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1461  // of the strip, selecting any other tab in the strip will cause that new
1462  // tab's opener relationship to be forgotten.
1463
1464  // Open a new tab again.
1465  TabContentsWrapper* new_tab_contents2 = CreateTabContents();
1466  strip.AddTabContents(new_tab_contents2, -1, PageTransition::TYPED,
1467                       TabStripModel::ADD_SELECTED);
1468
1469  // Now select the first tab.
1470  strip.SelectTabContentsAt(0, true);
1471
1472  // Now select the last tab.
1473  strip.SelectTabContentsAt(strip.count() - 1, true);
1474
1475  // Now close the last tab. The next adjacent should be selected.
1476  strip.CloseTabContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1477  EXPECT_EQ(page_d_contents, strip.GetTabContentsAt(strip.selected_index()));
1478
1479  strip.CloseAllTabs();
1480}
1481
1482// Tests that fast shutdown is attempted appropriately.
1483TEST_F(TabStripModelTest, FastShutdown) {
1484  TabStripDummyDelegate delegate(NULL);
1485  TabStripModel tabstrip(&delegate, profile());
1486  MockTabStripModelObserver observer;
1487  tabstrip.AddObserver(&observer);
1488
1489  EXPECT_TRUE(tabstrip.empty());
1490
1491  // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1492  // down.
1493  {
1494    TabContentsWrapper* contents1 = CreateTabContents();
1495    TabContentsWrapper* contents2 =
1496        CreateTabContentsWithSharedRPH(contents1->tab_contents());
1497
1498    SetID(contents1->tab_contents(), 1);
1499    SetID(contents2->tab_contents(), 2);
1500
1501    tabstrip.AppendTabContents(contents1, true);
1502    tabstrip.AppendTabContents(contents2, true);
1503
1504    // Turn on the fake unload listener so the tabs don't actually get shut
1505    // down when we call CloseAllTabs()---we need to be able to check that
1506    // fast shutdown was attempted.
1507    delegate.set_run_unload_listener(true);
1508    tabstrip.CloseAllTabs();
1509    // On a mock RPH this checks whether we *attempted* fast shutdown.
1510    // A real RPH would reject our attempt since there is an unload handler.
1511    EXPECT_TRUE(contents1->tab_contents()->
1512        GetRenderProcessHost()->fast_shutdown_started());
1513    EXPECT_EQ(2, tabstrip.count());
1514
1515    delegate.set_run_unload_listener(false);
1516    tabstrip.CloseAllTabs();
1517    EXPECT_TRUE(tabstrip.empty());
1518  }
1519
1520  // Make sure fast shutdown is not attempted when only some tabs that share a
1521  // RPH are shut down.
1522  {
1523    TabContentsWrapper* contents1 = CreateTabContents();
1524    TabContentsWrapper* contents2 =
1525        CreateTabContentsWithSharedRPH(contents1->tab_contents());
1526
1527    SetID(contents1->tab_contents(), 1);
1528    SetID(contents2->tab_contents(), 2);
1529
1530    tabstrip.AppendTabContents(contents1, true);
1531    tabstrip.AppendTabContents(contents2, true);
1532
1533    tabstrip.CloseTabContentsAt(1, TabStripModel::CLOSE_NONE);
1534    EXPECT_FALSE(contents1->tab_contents()->
1535        GetRenderProcessHost()->fast_shutdown_started());
1536    EXPECT_EQ(1, tabstrip.count());
1537
1538    tabstrip.CloseAllTabs();
1539    EXPECT_TRUE(tabstrip.empty());
1540  }
1541}
1542
1543// Tests various permutations of apps.
1544TEST_F(TabStripModelTest, Apps) {
1545  TabStripDummyDelegate delegate(NULL);
1546  TabStripModel tabstrip(&delegate, profile());
1547  MockTabStripModelObserver observer;
1548  tabstrip.AddObserver(&observer);
1549
1550  EXPECT_TRUE(tabstrip.empty());
1551
1552  typedef MockTabStripModelObserver::State State;
1553
1554#if defined(OS_WIN)
1555  FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1556#elif defined(OS_POSIX)
1557  FilePath path(FILE_PATH_LITERAL("/foo"));
1558#endif
1559  scoped_refptr<Extension> extension_app(new Extension(path,
1560                                                       Extension::INVALID));
1561  extension_app->launch_web_url_ = "http://www.google.com";
1562  TabContentsWrapper* contents1 = CreateTabContents();
1563  contents1->tab_contents()->SetExtensionApp(extension_app);
1564  TabContentsWrapper* contents2 = CreateTabContents();
1565  contents2->tab_contents()->SetExtensionApp(extension_app);
1566  TabContentsWrapper* contents3 = CreateTabContents();
1567
1568  SetID(contents1->tab_contents(), 1);
1569  SetID(contents2->tab_contents(), 2);
1570  SetID(contents3->tab_contents(), 3);
1571
1572  // Note! The ordering of these tests is important, each subsequent test
1573  // builds on the state established in the previous. This is important if you
1574  // ever insert tests rather than append.
1575
1576  // Initial state, tab3 only and selected.
1577  tabstrip.AppendTabContents(contents3, true);
1578
1579  observer.ClearStates();
1580
1581  // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1582  // position and tab1 should end up at position 0.
1583  {
1584    tabstrip.InsertTabContentsAt(1, contents1, TabStripModel::ADD_NONE);
1585
1586    ASSERT_EQ(1, observer.GetStateCount());
1587    State state(contents1, 0, MockTabStripModelObserver::INSERT);
1588    EXPECT_TRUE(observer.StateEquals(0, state));
1589
1590    // And verify the state.
1591    EXPECT_EQ("1ap 3", GetPinnedState(tabstrip));
1592
1593    observer.ClearStates();
1594  }
1595
1596  // Insert tab 2 at position 1.
1597  {
1598    tabstrip.InsertTabContentsAt(1, contents2, TabStripModel::ADD_NONE);
1599
1600    ASSERT_EQ(1, observer.GetStateCount());
1601    State state(contents2, 1, MockTabStripModelObserver::INSERT);
1602    EXPECT_TRUE(observer.StateEquals(0, state));
1603
1604    // And verify the state.
1605    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1606
1607    observer.ClearStates();
1608  }
1609
1610  // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1611  {
1612    tabstrip.MoveTabContentsAt(2, 0, false);
1613
1614    ASSERT_EQ(0, observer.GetStateCount());
1615
1616    // And verify the state didn't change.
1617    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1618
1619    observer.ClearStates();
1620  }
1621
1622  // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1623  {
1624    tabstrip.MoveTabContentsAt(0, 2, false);
1625
1626    ASSERT_EQ(0, observer.GetStateCount());
1627
1628    // And verify the state didn't change.
1629    EXPECT_EQ("1ap 2ap 3", GetPinnedState(tabstrip));
1630
1631    observer.ClearStates();
1632  }
1633
1634  // Try to move tab 0 to position 1. This is a legal move.
1635  {
1636    tabstrip.MoveTabContentsAt(0, 1, false);
1637
1638    ASSERT_EQ(1, observer.GetStateCount());
1639    State state(contents1, 1, MockTabStripModelObserver::MOVE);
1640    state.src_index = 0;
1641    EXPECT_TRUE(observer.StateEquals(0, state));
1642
1643    // And verify the state didn't change.
1644    EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip));
1645
1646    observer.ClearStates();
1647  }
1648
1649  // Remove tab3 and insert at position 0. It should be forced to position 2.
1650  {
1651    tabstrip.DetachTabContentsAt(2);
1652    observer.ClearStates();
1653
1654    tabstrip.InsertTabContentsAt(0, contents3, TabStripModel::ADD_NONE);
1655
1656    ASSERT_EQ(1, observer.GetStateCount());
1657    State state(contents3, 2, MockTabStripModelObserver::INSERT);
1658    EXPECT_TRUE(observer.StateEquals(0, state));
1659
1660    // And verify the state didn't change.
1661    EXPECT_EQ("2ap 1ap 3", GetPinnedState(tabstrip));
1662
1663    observer.ClearStates();
1664  }
1665
1666  tabstrip.CloseAllTabs();
1667}
1668
1669// Tests various permutations of pinning tabs.
1670TEST_F(TabStripModelTest, Pinning) {
1671  TabStripDummyDelegate delegate(NULL);
1672  TabStripModel tabstrip(&delegate, profile());
1673  MockTabStripModelObserver observer;
1674  tabstrip.AddObserver(&observer);
1675
1676  EXPECT_TRUE(tabstrip.empty());
1677
1678  typedef MockTabStripModelObserver::State State;
1679
1680  TabContentsWrapper* contents1 = CreateTabContents();
1681  TabContentsWrapper* contents2 = CreateTabContents();
1682  TabContentsWrapper* contents3 = CreateTabContents();
1683
1684  SetID(contents1->tab_contents(), 1);
1685  SetID(contents2->tab_contents(), 2);
1686  SetID(contents3->tab_contents(), 3);
1687
1688  // Note! The ordering of these tests is important, each subsequent test
1689  // builds on the state established in the previous. This is important if you
1690  // ever insert tests rather than append.
1691
1692  // Initial state, three tabs, first selected.
1693  tabstrip.AppendTabContents(contents1, true);
1694  tabstrip.AppendTabContents(contents2, false);
1695  tabstrip.AppendTabContents(contents3, false);
1696
1697  observer.ClearStates();
1698
1699  // Pin the first tab, this shouldn't visually reorder anything.
1700  {
1701    tabstrip.SetTabPinned(0, true);
1702
1703    // As the order didn't change, we should get a pinned notification.
1704    ASSERT_EQ(1, observer.GetStateCount());
1705    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1706    EXPECT_TRUE(observer.StateEquals(0, state));
1707
1708    // And verify the state.
1709    EXPECT_EQ("1p 2 3", GetPinnedState(tabstrip));
1710
1711    observer.ClearStates();
1712  }
1713
1714  // Unpin the first tab.
1715  {
1716    tabstrip.SetTabPinned(0, false);
1717
1718    // As the order didn't change, we should get a pinned notification.
1719    ASSERT_EQ(1, observer.GetStateCount());
1720    State state(contents1, 0, MockTabStripModelObserver::PINNED);
1721    EXPECT_TRUE(observer.StateEquals(0, state));
1722
1723    // And verify the state.
1724    EXPECT_EQ("1 2 3", GetPinnedState(tabstrip));
1725
1726    observer.ClearStates();
1727  }
1728
1729  // Pin the 3rd tab, which should move it to the front.
1730  {
1731    tabstrip.SetTabPinned(2, true);
1732
1733    // The pinning should have resulted in a move and a pinned notification.
1734    ASSERT_EQ(2, observer.GetStateCount());
1735    State state(contents3, 0, MockTabStripModelObserver::MOVE);
1736    state.src_index = 2;
1737    EXPECT_TRUE(observer.StateEquals(0, state));
1738
1739    state = State(contents3, 0, MockTabStripModelObserver::PINNED);
1740    EXPECT_TRUE(observer.StateEquals(1, state));
1741
1742    // And verify the state.
1743    EXPECT_EQ("3p 1 2", GetPinnedState(tabstrip));
1744
1745    observer.ClearStates();
1746  }
1747
1748  // Pin the tab "1", which shouldn't move anything.
1749  {
1750    tabstrip.SetTabPinned(1, true);
1751
1752    // As the order didn't change, we should get a pinned notification.
1753    ASSERT_EQ(1, observer.GetStateCount());
1754    State state(contents1, 1, MockTabStripModelObserver::PINNED);
1755    EXPECT_TRUE(observer.StateEquals(0, state));
1756
1757    // And verify the state.
1758    EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip));
1759
1760    observer.ClearStates();
1761  }
1762
1763  // Try to move tab "2" to the front, it should be ignored.
1764  {
1765    tabstrip.MoveTabContentsAt(2, 0, false);
1766
1767    // As the order didn't change, we should get a pinned notification.
1768    ASSERT_EQ(0, observer.GetStateCount());
1769
1770    // And verify the state.
1771    EXPECT_EQ("3p 1p 2", GetPinnedState(tabstrip));
1772
1773    observer.ClearStates();
1774  }
1775
1776  // Unpin tab "3", which implicitly moves it to the end.
1777  {
1778    tabstrip.SetTabPinned(0, false);
1779
1780    ASSERT_EQ(2, observer.GetStateCount());
1781    State state(contents3, 1, MockTabStripModelObserver::MOVE);
1782    state.src_index = 0;
1783    EXPECT_TRUE(observer.StateEquals(0, state));
1784
1785    state = State(contents3, 1, MockTabStripModelObserver::PINNED);
1786    EXPECT_TRUE(observer.StateEquals(1, state));
1787
1788    // And verify the state.
1789    EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip));
1790
1791    observer.ClearStates();
1792  }
1793
1794  // Unpin tab "3", nothing should happen.
1795  {
1796    tabstrip.SetTabPinned(1, false);
1797
1798    ASSERT_EQ(0, observer.GetStateCount());
1799
1800    EXPECT_EQ("1p 3 2", GetPinnedState(tabstrip));
1801
1802    observer.ClearStates();
1803  }
1804
1805  // Pin "3" and "1".
1806  {
1807    tabstrip.SetTabPinned(0, true);
1808    tabstrip.SetTabPinned(1, true);
1809
1810    EXPECT_EQ("1p 3p 2", GetPinnedState(tabstrip));
1811
1812    observer.ClearStates();
1813  }
1814
1815  TabContentsWrapper* contents4 = CreateTabContents();
1816  SetID(contents4->tab_contents(), 4);
1817
1818  // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
1819  // up after them.
1820  {
1821    tabstrip.InsertTabContentsAt(1, contents4, TabStripModel::ADD_NONE);
1822
1823    ASSERT_EQ(1, observer.GetStateCount());
1824    State state(contents4, 2, MockTabStripModelObserver::INSERT);
1825    EXPECT_TRUE(observer.StateEquals(0, state));
1826
1827    EXPECT_EQ("1p 3p 4 2", GetPinnedState(tabstrip));
1828  }
1829
1830  tabstrip.CloseAllTabs();
1831}
1832
1833// Makes sure the TabStripModel calls the right observer methods during a
1834// replace.
1835TEST_F(TabStripModelTest, ReplaceSendsSelected) {
1836  typedef MockTabStripModelObserver::State State;
1837
1838  TabStripDummyDelegate delegate(NULL);
1839  TabStripModel strip(&delegate, profile());
1840
1841  TabContentsWrapper* first_contents = CreateTabContents();
1842  strip.AddTabContents(first_contents, -1, PageTransition::TYPED,
1843                       TabStripModel::ADD_SELECTED);
1844
1845  MockTabStripModelObserver tabstrip_observer;
1846  strip.AddObserver(&tabstrip_observer);
1847
1848  TabContentsWrapper* new_contents = CreateTabContents();
1849  delete strip.ReplaceTabContentsAt(0, new_contents);
1850
1851  ASSERT_EQ(2, tabstrip_observer.GetStateCount());
1852
1853  // First event should be for replaced.
1854  State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
1855  state.src_contents = first_contents;
1856  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
1857
1858  // And the second for selected.
1859  state = State(new_contents, 0, MockTabStripModelObserver::SELECT);
1860  state.src_contents = first_contents;
1861  EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
1862
1863  // Now add another tab and replace it, making sure we don't get a selected
1864  // event this time.
1865  TabContentsWrapper* third_contents = CreateTabContents();
1866  strip.AddTabContents(third_contents, 1, PageTransition::TYPED,
1867                       TabStripModel::ADD_NONE);
1868
1869  tabstrip_observer.ClearStates();
1870
1871  // And replace it.
1872  new_contents = CreateTabContents();
1873  delete strip.ReplaceTabContentsAt(1, new_contents);
1874
1875  ASSERT_EQ(1, tabstrip_observer.GetStateCount());
1876
1877  state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
1878  state.src_contents = third_contents;
1879  EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
1880
1881  strip.CloseAllTabs();
1882}
1883
1884// Makes sure TabStripModel handles the case of deleting a tab while removing
1885// another tab.
1886TEST_F(TabStripModelTest, DeleteFromDestroy) {
1887  TabStripDummyDelegate delegate(NULL);
1888  TabStripModel strip(&delegate, profile());
1889  TabContentsWrapper* contents1 = CreateTabContents();
1890  TabContentsWrapper* contents2 = CreateTabContents();
1891  strip.AppendTabContents(contents1, true);
1892  strip.AppendTabContents(contents2, true);
1893  // DeleteTabContentsOnDestroyedObserver deletes contents1 when contents2 sends
1894  // out notification that it is being destroyed.
1895  DeleteTabContentsOnDestroyedObserver observer(contents2, contents1);
1896  strip.CloseAllTabs();
1897}
1898