Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / tabs / tab_strip_model_unittest.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 <map>
8 #include <string>
9
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/defaults.h"
19 #include "chrome/browser/extensions/tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tabstrip.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
25 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/web_modal/web_contents_modal_dialog_manager.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_observer.h"
36 #include "extensions/common/extension.h"
37 #include "testing/gtest/include/gtest/gtest.h"
38
39 using content::SiteInstance;
40 using content::WebContents;
41 using extensions::Extension;
42 using web_modal::NativeWebContentsModalDialog;
43
44 namespace {
45
46 // Class used to delete a WebContents and TabStripModel when another WebContents
47 // is destroyed.
48 class DeleteWebContentsOnDestroyedObserver
49     : public content::WebContentsObserver {
50  public:
51   // When |source| is deleted both |tab_to_delete| and |tab_strip| are deleted.
52   // |tab_to_delete| and |tab_strip| may be NULL.
53   DeleteWebContentsOnDestroyedObserver(WebContents* source,
54                                        WebContents* tab_to_delete,
55                                        TabStripModel* tab_strip)
56       : WebContentsObserver(source),
57         tab_to_delete_(tab_to_delete),
58         tab_strip_(tab_strip) {
59   }
60
61   virtual void WebContentsDestroyed() OVERRIDE {
62     WebContents* tab_to_delete = tab_to_delete_;
63     tab_to_delete_ = NULL;
64     TabStripModel* tab_strip_to_delete = tab_strip_;
65     tab_strip_ = NULL;
66     delete tab_to_delete;
67     delete tab_strip_to_delete;
68   }
69
70  private:
71   WebContents* tab_to_delete_;
72   TabStripModel* tab_strip_;
73
74   DISALLOW_COPY_AND_ASSIGN(DeleteWebContentsOnDestroyedObserver);
75 };
76
77 class TabStripDummyDelegate : public TestTabStripModelDelegate {
78  public:
79   TabStripDummyDelegate() : run_unload_(false) {}
80   virtual ~TabStripDummyDelegate() {}
81
82   void set_run_unload_listener(bool value) { run_unload_ = value; }
83
84   virtual bool RunUnloadListenerBeforeClosing(WebContents* contents) OVERRIDE {
85     return run_unload_;
86   }
87
88  private:
89   // Whether to report that we need to run an unload listener before closing.
90   bool run_unload_;
91
92   DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate);
93 };
94
95 const char kTabStripModelTestIDUserDataKey[] = "TabStripModelTestIDUserData";
96
97 class TabStripModelTestIDUserData : public base::SupportsUserData::Data {
98  public:
99   explicit TabStripModelTestIDUserData(int id) : id_(id) {}
100   virtual ~TabStripModelTestIDUserData() {}
101   int id() { return id_; }
102
103  private:
104   int id_;
105 };
106
107 class DummySingleWebContentsDialogManager
108     : public web_modal::SingleWebContentsDialogManager {
109  public:
110   explicit DummySingleWebContentsDialogManager(
111       NativeWebContentsModalDialog dialog,
112       web_modal::SingleWebContentsDialogManagerDelegate* delegate)
113       : delegate_(delegate),
114         dialog_(dialog) {}
115   virtual ~DummySingleWebContentsDialogManager() {}
116
117   virtual void Show() OVERRIDE {}
118   virtual void Hide() OVERRIDE {}
119   virtual void Close() OVERRIDE {
120     delegate_->WillClose(dialog_);
121   }
122   virtual void Focus() OVERRIDE {}
123   virtual void Pulse() OVERRIDE {}
124   virtual void HostChanged(
125       web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {}
126   virtual NativeWebContentsModalDialog dialog() OVERRIDE { return dialog_; }
127
128  private:
129   web_modal::SingleWebContentsDialogManagerDelegate* delegate_;
130   NativeWebContentsModalDialog dialog_;
131
132   DISALLOW_COPY_AND_ASSIGN(DummySingleWebContentsDialogManager);
133 };
134
135 // Test Browser-like class for TabStripModelTest.TabBlockedState.
136 class TabBlockedStateTestBrowser
137     : public TabStripModelObserver,
138       public web_modal::WebContentsModalDialogManagerDelegate {
139  public:
140   explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model)
141       : tab_strip_model_(tab_strip_model) {
142     tab_strip_model_->AddObserver(this);
143   }
144
145   virtual ~TabBlockedStateTestBrowser() {
146     tab_strip_model_->RemoveObserver(this);
147   }
148
149  private:
150   // TabStripModelObserver
151   virtual void TabInsertedAt(WebContents* contents,
152                              int index,
153                              bool foreground) OVERRIDE {
154     web_modal::WebContentsModalDialogManager* manager =
155         web_modal::WebContentsModalDialogManager::FromWebContents(contents);
156     if (manager)
157       manager->SetDelegate(this);
158   }
159
160   // WebContentsModalDialogManagerDelegate
161   virtual void SetWebContentsBlocked(content::WebContents* contents,
162                                      bool blocked) OVERRIDE {
163     int index = tab_strip_model_->GetIndexOfWebContents(contents);
164     ASSERT_GE(index, 0);
165     tab_strip_model_->SetTabBlocked(index, blocked);
166   }
167
168   TabStripModel* tab_strip_model_;
169
170   DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
171 };
172
173 }  // namespace
174
175 class TabStripModelTest : public ChromeRenderViewHostTestHarness {
176  public:
177   WebContents* CreateWebContents() {
178     return WebContents::Create(WebContents::CreateParams(profile()));
179   }
180
181   WebContents* CreateWebContentsWithSharedRPH(WebContents* web_contents) {
182     WebContents::CreateParams create_params(
183         profile(), web_contents->GetRenderViewHost()->GetSiteInstance());
184     WebContents* retval = WebContents::Create(create_params);
185     EXPECT_EQ(retval->GetRenderProcessHost(),
186               web_contents->GetRenderProcessHost());
187     return retval;
188   }
189
190   // Sets the id of the specified contents.
191   void SetID(WebContents* contents, int id) {
192     contents->SetUserData(&kTabStripModelTestIDUserDataKey,
193                           new TabStripModelTestIDUserData(id));
194   }
195
196   // Returns the id of the specified contents.
197   int GetID(WebContents* contents) {
198     TabStripModelTestIDUserData* user_data =
199         static_cast<TabStripModelTestIDUserData*>(
200             contents->GetUserData(&kTabStripModelTestIDUserDataKey));
201
202     return user_data ? user_data->id() : -1;
203   }
204
205   // Returns the state of the given tab strip as a string. The state consists
206   // of the ID of each web contents followed by a 'p' if pinned. For example,
207   // if the model consists of two tabs with ids 2 and 1, with the first
208   // tab pinned, this returns "2p 1".
209   std::string GetTabStripStateString(const TabStripModel& model) {
210     std::string actual;
211     for (int i = 0; i < model.count(); ++i) {
212       if (i > 0)
213         actual += " ";
214
215       actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
216
217       if (model.IsAppTab(i))
218         actual += "a";
219
220       if (model.IsTabPinned(i))
221         actual += "p";
222     }
223     return actual;
224   }
225
226   std::string GetIndicesClosedByCommandAsString(
227       const TabStripModel& model,
228       int index,
229       TabStripModel::ContextMenuCommand id) const {
230     std::vector<int> indices = model.GetIndicesClosedByCommand(index, id);
231     std::string result;
232     for (size_t i = 0; i < indices.size(); ++i) {
233       if (i != 0)
234         result += " ";
235       result += base::IntToString(indices[i]);
236     }
237     return result;
238   }
239
240   void PrepareTabstripForSelectionTest(TabStripModel* model,
241                                        int tab_count,
242                                        int pinned_count,
243                                        const std::string& selected_tabs) {
244     for (int i = 0; i < tab_count; ++i) {
245       WebContents* contents = CreateWebContents();
246       SetID(contents, i);
247       model->AppendWebContents(contents, true);
248     }
249     for (int i = 0; i < pinned_count; ++i)
250       model->SetTabPinned(i, true);
251
252     ui::ListSelectionModel selection_model;
253     std::vector<std::string> selection;
254     base::SplitStringAlongWhitespace(selected_tabs, &selection);
255     for (size_t i = 0; i < selection.size(); ++i) {
256       int value;
257       ASSERT_TRUE(base::StringToInt(selection[i], &value));
258       selection_model.AddIndexToSelection(value);
259     }
260     selection_model.set_active(selection_model.selected_indices()[0]);
261     model->SetSelectionFromModel(selection_model);
262   }
263 };
264
265 class MockTabStripModelObserver : public TabStripModelObserver {
266  public:
267   explicit MockTabStripModelObserver(TabStripModel* model)
268       : empty_(true),
269         deleted_(false),
270         model_(model) {}
271   virtual ~MockTabStripModelObserver() {}
272
273   enum TabStripModelObserverAction {
274     INSERT,
275     CLOSE,
276     DETACH,
277     ACTIVATE,
278     DEACTIVATE,
279     SELECT,
280     MOVE,
281     CHANGE,
282     PINNED,
283     REPLACED,
284     CLOSE_ALL,
285     CLOSE_ALL_CANCELED,
286   };
287
288   struct State {
289     State(WebContents* a_dst_contents,
290           int a_dst_index,
291           TabStripModelObserverAction a_action)
292         : src_contents(NULL),
293           dst_contents(a_dst_contents),
294           src_index(-1),
295           dst_index(a_dst_index),
296           change_reason(CHANGE_REASON_NONE),
297           foreground(false),
298           action(a_action) {
299     }
300
301     WebContents* src_contents;
302     WebContents* dst_contents;
303     int src_index;
304     int dst_index;
305     int change_reason;
306     bool foreground;
307     TabStripModelObserverAction action;
308   };
309
310   int GetStateCount() const {
311     return static_cast<int>(states_.size());
312   }
313
314   // Returns (by way of parameters) the number of state's with CLOSE_ALL and
315   // CLOSE_ALL_CANCELED.
316   void GetCloseCounts(int* close_all_count,
317                       int* close_all_canceled_count) {
318     *close_all_count = *close_all_canceled_count = 0;
319     for (int i = 0; i < GetStateCount(); ++i) {
320       switch (GetStateAt(i).action) {
321         case CLOSE_ALL:
322           (*close_all_count)++;
323           break;
324         case CLOSE_ALL_CANCELED:
325           (*close_all_canceled_count)++;
326           break;
327         default:
328           break;
329       }
330     }
331   }
332
333   const State& GetStateAt(int index) const {
334     DCHECK(index >= 0 && index < GetStateCount());
335     return states_[index];
336   }
337
338   bool StateEquals(int index, const State& state) {
339     const State& s = GetStateAt(index);
340     return (s.src_contents == state.src_contents &&
341             s.dst_contents == state.dst_contents &&
342             s.src_index == state.src_index &&
343             s.dst_index == state.dst_index &&
344             s.change_reason == state.change_reason &&
345             s.foreground == state.foreground &&
346             s.action == state.action);
347   }
348
349   // TabStripModelObserver implementation:
350   virtual void TabInsertedAt(WebContents* contents,
351                              int index,
352                              bool foreground) OVERRIDE {
353     empty_ = false;
354     State s(contents, index, INSERT);
355     s.foreground = foreground;
356     states_.push_back(s);
357   }
358   virtual void ActiveTabChanged(WebContents* old_contents,
359                                 WebContents* new_contents,
360                                 int index,
361                                 int reason) OVERRIDE {
362     State s(new_contents, index, ACTIVATE);
363     s.src_contents = old_contents;
364     s.change_reason = reason;
365     states_.push_back(s);
366   }
367   virtual void TabSelectionChanged(
368       TabStripModel* tab_strip_model,
369       const ui::ListSelectionModel& old_model) OVERRIDE {
370     State s(model()->GetActiveWebContents(), model()->active_index(), SELECT);
371     s.src_contents = model()->GetWebContentsAt(old_model.active());
372     s.src_index = old_model.active();
373     states_.push_back(s);
374   }
375   virtual void TabMoved(WebContents* contents,
376                         int from_index,
377                         int to_index) OVERRIDE {
378     State s(contents, to_index, MOVE);
379     s.src_index = from_index;
380     states_.push_back(s);
381   }
382
383   virtual void TabClosingAt(TabStripModel* tab_strip_model,
384                             WebContents* contents,
385                             int index) OVERRIDE {
386     states_.push_back(State(contents, index, CLOSE));
387   }
388   virtual void TabDetachedAt(WebContents* contents, int index) OVERRIDE {
389     states_.push_back(State(contents, index, DETACH));
390   }
391   virtual void TabDeactivated(WebContents* contents) OVERRIDE {
392     states_.push_back(State(contents, model()->active_index(), DEACTIVATE));
393   }
394   virtual void TabChangedAt(WebContents* contents,
395                             int index,
396                             TabChangeType change_type) OVERRIDE {
397     states_.push_back(State(contents, index, CHANGE));
398   }
399   virtual void TabReplacedAt(TabStripModel* tab_strip_model,
400                              WebContents* old_contents,
401                              WebContents* new_contents,
402                              int index) OVERRIDE {
403     State s(new_contents, index, REPLACED);
404     s.src_contents = old_contents;
405     states_.push_back(s);
406   }
407   virtual void TabPinnedStateChanged(WebContents* contents,
408                                      int index) OVERRIDE {
409     states_.push_back(State(contents, index, PINNED));
410   }
411   virtual void TabStripEmpty() OVERRIDE {
412     empty_ = true;
413   }
414   virtual void WillCloseAllTabs() OVERRIDE {
415     states_.push_back(State(NULL, -1, CLOSE_ALL));
416   }
417   virtual void CloseAllTabsCanceled() OVERRIDE {
418     states_.push_back(State(NULL, -1, CLOSE_ALL_CANCELED));
419   }
420   virtual void TabStripModelDeleted() OVERRIDE {
421     deleted_ = true;
422   }
423
424   void ClearStates() {
425     states_.clear();
426   }
427
428   bool empty() const { return empty_; }
429   bool deleted() const { return deleted_; }
430   TabStripModel* model() { return model_; }
431
432  private:
433   std::vector<State> states_;
434
435   bool empty_;
436   bool deleted_;
437   TabStripModel* model_;
438
439   DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
440 };
441
442 TEST_F(TabStripModelTest, TestBasicAPI) {
443   TabStripDummyDelegate delegate;
444   TabStripModel tabstrip(&delegate, profile());
445   MockTabStripModelObserver observer(&tabstrip);
446   tabstrip.AddObserver(&observer);
447
448   EXPECT_TRUE(tabstrip.empty());
449
450   typedef MockTabStripModelObserver::State State;
451
452   WebContents* contents1 = CreateWebContents();
453   SetID(contents1, 1);
454
455   // Note! The ordering of these tests is important, each subsequent test
456   // builds on the state established in the previous. This is important if you
457   // ever insert tests rather than append.
458
459   // Test AppendWebContents, ContainsIndex
460   {
461     EXPECT_FALSE(tabstrip.ContainsIndex(0));
462     tabstrip.AppendWebContents(contents1, true);
463     EXPECT_TRUE(tabstrip.ContainsIndex(0));
464     EXPECT_EQ(1, tabstrip.count());
465     EXPECT_EQ(3, observer.GetStateCount());
466     State s1(contents1, 0, MockTabStripModelObserver::INSERT);
467     s1.foreground = true;
468     EXPECT_TRUE(observer.StateEquals(0, s1));
469     State s2(contents1, 0, MockTabStripModelObserver::ACTIVATE);
470     EXPECT_TRUE(observer.StateEquals(1, s2));
471     State s3(contents1, 0, MockTabStripModelObserver::SELECT);
472     s3.src_contents = NULL;
473     s3.src_index = ui::ListSelectionModel::kUnselectedIndex;
474     EXPECT_TRUE(observer.StateEquals(2, s3));
475     observer.ClearStates();
476   }
477   EXPECT_EQ("1", GetTabStripStateString(tabstrip));
478
479   // Test InsertWebContentsAt, foreground tab.
480   WebContents* contents2 = CreateWebContents();
481   SetID(contents2, 2);
482   {
483     tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_ACTIVE);
484
485     EXPECT_EQ(2, tabstrip.count());
486     EXPECT_EQ(4, observer.GetStateCount());
487     State s1(contents2, 1, MockTabStripModelObserver::INSERT);
488     s1.foreground = true;
489     EXPECT_TRUE(observer.StateEquals(0, s1));
490     State s2(contents1, 0, MockTabStripModelObserver::DEACTIVATE);
491     EXPECT_TRUE(observer.StateEquals(1, s2));
492     State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
493     s3.src_contents = contents1;
494     EXPECT_TRUE(observer.StateEquals(2, s3));
495     State s4(contents2, 1, MockTabStripModelObserver::SELECT);
496     s4.src_contents = contents1;
497     s4.src_index = 0;
498     EXPECT_TRUE(observer.StateEquals(3, s4));
499     observer.ClearStates();
500   }
501   EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
502
503   // Test InsertWebContentsAt, background tab.
504   WebContents* contents3 = CreateWebContents();
505   SetID(contents3, 3);
506   {
507     tabstrip.InsertWebContentsAt(2, contents3, TabStripModel::ADD_NONE);
508
509     EXPECT_EQ(3, tabstrip.count());
510     EXPECT_EQ(1, observer.GetStateCount());
511     State s1(contents3, 2, MockTabStripModelObserver::INSERT);
512     s1.foreground = false;
513     EXPECT_TRUE(observer.StateEquals(0, s1));
514     observer.ClearStates();
515   }
516   EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
517
518   // Test ActivateTabAt
519   {
520     tabstrip.ActivateTabAt(2, true);
521     EXPECT_EQ(3, observer.GetStateCount());
522     State s1(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
523     EXPECT_TRUE(observer.StateEquals(0, s1));
524     State s2(contents3, 2, MockTabStripModelObserver::ACTIVATE);
525     s2.src_contents = contents2;
526     s2.change_reason = TabStripModelObserver::CHANGE_REASON_USER_GESTURE;
527     EXPECT_TRUE(observer.StateEquals(1, s2));
528     State s3(contents3, 2, MockTabStripModelObserver::SELECT);
529     s3.src_contents = contents2;
530     s3.src_index = 1;
531     EXPECT_TRUE(observer.StateEquals(2, s3));
532     observer.ClearStates();
533   }
534   EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
535
536   // Test DetachWebContentsAt
537   {
538     // Detach ...
539     WebContents* detached = tabstrip.DetachWebContentsAt(2);
540     // ... and append again because we want this for later.
541     tabstrip.AppendWebContents(detached, true);
542     EXPECT_EQ(8, observer.GetStateCount());
543     State s1(detached, 2, MockTabStripModelObserver::DETACH);
544     EXPECT_TRUE(observer.StateEquals(0, s1));
545     State s2(detached, ui::ListSelectionModel::kUnselectedIndex,
546         MockTabStripModelObserver::DEACTIVATE);
547     EXPECT_TRUE(observer.StateEquals(1, s2));
548     State s3(contents2, 1, MockTabStripModelObserver::ACTIVATE);
549     s3.src_contents = contents3;
550     s3.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
551     EXPECT_TRUE(observer.StateEquals(2, s3));
552     State s4(contents2, 1, MockTabStripModelObserver::SELECT);
553     s4.src_contents = NULL;
554     s4.src_index = ui::ListSelectionModel::kUnselectedIndex;
555     EXPECT_TRUE(observer.StateEquals(3, s4));
556     State s5(detached, 2, MockTabStripModelObserver::INSERT);
557     s5.foreground = true;
558     EXPECT_TRUE(observer.StateEquals(4, s5));
559     State s6(contents2, 1, MockTabStripModelObserver::DEACTIVATE);
560     EXPECT_TRUE(observer.StateEquals(5, s6));
561     State s7(detached, 2, MockTabStripModelObserver::ACTIVATE);
562     s7.src_contents = contents2;
563     s7.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
564     EXPECT_TRUE(observer.StateEquals(6, s7));
565     State s8(detached, 2, MockTabStripModelObserver::SELECT);
566     s8.src_contents = contents2;
567     s8.src_index = 1;
568     EXPECT_TRUE(observer.StateEquals(7, s8));
569     observer.ClearStates();
570   }
571   EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
572
573   // Test CloseWebContentsAt
574   {
575     EXPECT_TRUE(tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE));
576     EXPECT_EQ(2, tabstrip.count());
577
578     EXPECT_EQ(5, observer.GetStateCount());
579     State s1(contents3, 2, MockTabStripModelObserver::CLOSE);
580     EXPECT_TRUE(observer.StateEquals(0, s1));
581     State s2(contents3, 2, MockTabStripModelObserver::DETACH);
582     EXPECT_TRUE(observer.StateEquals(1, s2));
583     State s3(contents3, ui::ListSelectionModel::kUnselectedIndex,
584         MockTabStripModelObserver::DEACTIVATE);
585     EXPECT_TRUE(observer.StateEquals(2, s3));
586     State s4(contents2, 1, MockTabStripModelObserver::ACTIVATE);
587     s4.src_contents = contents3;
588     s4.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
589     EXPECT_TRUE(observer.StateEquals(3, s4));
590     State s5(contents2, 1, MockTabStripModelObserver::SELECT);
591     s5.src_contents = NULL;
592     s5.src_index = ui::ListSelectionModel::kUnselectedIndex;
593     EXPECT_TRUE(observer.StateEquals(4, s5));
594     observer.ClearStates();
595   }
596   EXPECT_EQ("1 2", GetTabStripStateString(tabstrip));
597
598   // Test MoveWebContentsAt, select_after_move == true
599   {
600     tabstrip.MoveWebContentsAt(1, 0, true);
601
602     EXPECT_EQ(1, observer.GetStateCount());
603     State s1(contents2, 0, MockTabStripModelObserver::MOVE);
604     s1.src_index = 1;
605     EXPECT_TRUE(observer.StateEquals(0, s1));
606     EXPECT_EQ(0, tabstrip.active_index());
607     observer.ClearStates();
608   }
609   EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
610
611   // Test MoveWebContentsAt, select_after_move == false
612   {
613     tabstrip.MoveWebContentsAt(1, 0, false);
614     EXPECT_EQ(1, observer.GetStateCount());
615     State s1(contents1, 0, MockTabStripModelObserver::MOVE);
616     s1.src_index = 1;
617     EXPECT_TRUE(observer.StateEquals(0, s1));
618     EXPECT_EQ(1, tabstrip.active_index());
619
620     tabstrip.MoveWebContentsAt(0, 1, false);
621     observer.ClearStates();
622   }
623   EXPECT_EQ("2 1", GetTabStripStateString(tabstrip));
624
625   // Test Getters
626   {
627     EXPECT_EQ(contents2, tabstrip.GetActiveWebContents());
628     EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(0));
629     EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
630     EXPECT_EQ(0, tabstrip.GetIndexOfWebContents(contents2));
631     EXPECT_EQ(1, tabstrip.GetIndexOfWebContents(contents1));
632   }
633
634   // Test UpdateWebContentsStateAt
635   {
636     tabstrip.UpdateWebContentsStateAt(0, TabStripModelObserver::ALL);
637     EXPECT_EQ(1, observer.GetStateCount());
638     State s1(contents2, 0, MockTabStripModelObserver::CHANGE);
639     EXPECT_TRUE(observer.StateEquals(0, s1));
640     observer.ClearStates();
641   }
642
643   // Test SelectNextTab, SelectPreviousTab, SelectLastTab
644   {
645     // Make sure the second of the two tabs is selected first...
646     tabstrip.ActivateTabAt(1, true);
647     tabstrip.SelectPreviousTab();
648     EXPECT_EQ(0, tabstrip.active_index());
649     tabstrip.SelectLastTab();
650     EXPECT_EQ(1, tabstrip.active_index());
651     tabstrip.SelectNextTab();
652     EXPECT_EQ(0, tabstrip.active_index());
653   }
654
655   // Test CloseSelectedTabs
656   {
657     tabstrip.CloseSelectedTabs();
658     // |CloseSelectedTabs| calls CloseWebContentsAt, we already tested that, now
659     // just verify that the count and selected index have changed
660     // appropriately...
661     EXPECT_EQ(1, tabstrip.count());
662     EXPECT_EQ(0, tabstrip.active_index());
663   }
664
665   observer.ClearStates();
666   tabstrip.CloseAllTabs();
667
668   int close_all_count = 0, close_all_canceled_count = 0;
669   observer.GetCloseCounts(&close_all_count, &close_all_canceled_count);
670   EXPECT_EQ(1, close_all_count);
671   EXPECT_EQ(0, close_all_canceled_count);
672
673   // TabStripModel should now be empty.
674   EXPECT_TRUE(tabstrip.empty());
675
676   // Opener methods are tested below...
677
678   tabstrip.RemoveObserver(&observer);
679 }
680
681 TEST_F(TabStripModelTest, TestBasicOpenerAPI) {
682   TabStripDummyDelegate delegate;
683   TabStripModel tabstrip(&delegate, profile());
684   EXPECT_TRUE(tabstrip.empty());
685
686   // This is a basic test of opener functionality. opener is created
687   // as the first tab in the strip and then we create 5 other tabs in the
688   // background with opener set as their opener.
689
690   WebContents* opener = CreateWebContents();
691   tabstrip.AppendWebContents(opener, true);
692   WebContents* contents1 = CreateWebContents();
693   WebContents* contents2 = CreateWebContents();
694   WebContents* contents3 = CreateWebContents();
695   WebContents* contents4 = CreateWebContents();
696   WebContents* contents5 = CreateWebContents();
697
698   // We use |InsertWebContentsAt| here instead of |AppendWebContents| so that
699   // openership relationships are preserved.
700   tabstrip.InsertWebContentsAt(tabstrip.count(), contents1,
701                                TabStripModel::ADD_INHERIT_GROUP);
702   tabstrip.InsertWebContentsAt(tabstrip.count(), contents2,
703                                TabStripModel::ADD_INHERIT_GROUP);
704   tabstrip.InsertWebContentsAt(tabstrip.count(), contents3,
705                                TabStripModel::ADD_INHERIT_GROUP);
706   tabstrip.InsertWebContentsAt(tabstrip.count(), contents4,
707                                TabStripModel::ADD_INHERIT_GROUP);
708   tabstrip.InsertWebContentsAt(tabstrip.count(), contents5,
709                                TabStripModel::ADD_INHERIT_GROUP);
710
711   // All the tabs should have the same opener.
712   for (int i = 1; i < tabstrip.count(); ++i)
713     EXPECT_EQ(opener, tabstrip.GetOpenerOfWebContentsAt(i));
714
715   // If there is a next adjacent item, then the index should be of that item.
716   EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
717   // If the last tab in the group is closed, the preceding tab in the same
718   // group should be selected.
719   EXPECT_EQ(4, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
720
721   // Tests the method that finds the last tab opened by the same opener in the
722   // strip (this is the insertion index for the next background tab for the
723   // specified opener).
724   EXPECT_EQ(5, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
725
726   // For a tab that has opened no other tabs, the return value should always be
727   // -1...
728   EXPECT_EQ(-1,
729             tabstrip.GetIndexOfNextWebContentsOpenedBy(contents1, 3, false));
730   EXPECT_EQ(-1,
731             tabstrip.GetIndexOfLastWebContentsOpenedBy(contents1, 3));
732
733   // ForgetAllOpeners should destroy all opener relationships.
734   tabstrip.ForgetAllOpeners();
735   EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 1, false));
736   EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 5, false));
737   EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
738
739   // Specify the last tab as the opener of the others.
740   for (int i = 0; i < tabstrip.count() - 1; ++i)
741     tabstrip.SetOpenerOfWebContentsAt(i, contents5);
742
743   for (int i = 0; i < tabstrip.count() - 1; ++i)
744     EXPECT_EQ(contents5, tabstrip.GetOpenerOfWebContentsAt(i));
745
746   // If there is a next adjacent item, then the index should be of that item.
747   EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 1, false));
748
749   // If the last tab in the group is closed, the preceding tab in the same
750   // group should be selected.
751   EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(contents5, 4, false));
752
753   tabstrip.CloseAllTabs();
754   EXPECT_TRUE(tabstrip.empty());
755 }
756
757 static int GetInsertionIndex(TabStripModel* tabstrip) {
758   return tabstrip->order_controller()->DetermineInsertionIndex(
759       content::PAGE_TRANSITION_LINK, false);
760 }
761
762 static void InsertWebContentses(TabStripModel* tabstrip,
763                                 WebContents* contents1,
764                                 WebContents* contents2,
765                                 WebContents* contents3) {
766   tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
767                                 contents1,
768                                 TabStripModel::ADD_INHERIT_GROUP);
769   tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
770                                 contents2,
771                                 TabStripModel::ADD_INHERIT_GROUP);
772   tabstrip->InsertWebContentsAt(GetInsertionIndex(tabstrip),
773                                 contents3,
774                                 TabStripModel::ADD_INHERIT_GROUP);
775 }
776
777 // Tests opening background tabs.
778 TEST_F(TabStripModelTest, TestLTRInsertionOptions) {
779   TabStripDummyDelegate delegate;
780   TabStripModel tabstrip(&delegate, profile());
781   EXPECT_TRUE(tabstrip.empty());
782
783   WebContents* opener = CreateWebContents();
784   tabstrip.AppendWebContents(opener, true);
785
786   WebContents* contents1 = CreateWebContents();
787   WebContents* contents2 = CreateWebContents();
788   WebContents* contents3 = CreateWebContents();
789
790   // Test LTR
791   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
792   EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
793   EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
794   EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
795
796   tabstrip.CloseAllTabs();
797   EXPECT_TRUE(tabstrip.empty());
798 }
799
800 // This test constructs a tabstrip, and then simulates loading several tabs in
801 // the background from link clicks on the first tab. Then it simulates opening
802 // a new tab from the first tab in the foreground via a link click, verifies
803 // that this tab is opened adjacent to the opener, then closes it.
804 // Finally it tests that a tab opened for some non-link purpose opens at the
805 // end of the strip, not bundled to any existing context.
806 TEST_F(TabStripModelTest, TestInsertionIndexDetermination) {
807   TabStripDummyDelegate delegate;
808   TabStripModel tabstrip(&delegate, profile());
809   EXPECT_TRUE(tabstrip.empty());
810
811   WebContents* opener = CreateWebContents();
812   tabstrip.AppendWebContents(opener, true);
813
814   // Open some other random unrelated tab in the background to monkey with our
815   // insertion index.
816   WebContents* other = CreateWebContents();
817   tabstrip.AppendWebContents(other, false);
818
819   WebContents* contents1 = CreateWebContents();
820   WebContents* contents2 = CreateWebContents();
821   WebContents* contents3 = CreateWebContents();
822
823   // Start by testing LTR.
824   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
825   EXPECT_EQ(opener, tabstrip.GetWebContentsAt(0));
826   EXPECT_EQ(contents1, tabstrip.GetWebContentsAt(1));
827   EXPECT_EQ(contents2, tabstrip.GetWebContentsAt(2));
828   EXPECT_EQ(contents3, tabstrip.GetWebContentsAt(3));
829   EXPECT_EQ(other, tabstrip.GetWebContentsAt(4));
830
831   // The opener API should work...
832   EXPECT_EQ(3, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
833   EXPECT_EQ(2, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
834   EXPECT_EQ(3, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
835
836   // Now open a foreground tab from a link. It should be opened adjacent to the
837   // opener tab.
838   WebContents* fg_link_contents = CreateWebContents();
839   int insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
840       content::PAGE_TRANSITION_LINK, true);
841   EXPECT_EQ(1, insert_index);
842   tabstrip.InsertWebContentsAt(insert_index, fg_link_contents,
843                                TabStripModel::ADD_ACTIVE |
844                                TabStripModel::ADD_INHERIT_GROUP);
845   EXPECT_EQ(1, tabstrip.active_index());
846   EXPECT_EQ(fg_link_contents, tabstrip.GetActiveWebContents());
847
848   // Now close this contents. The selection should move to the opener contents.
849   tabstrip.CloseSelectedTabs();
850   EXPECT_EQ(0, tabstrip.active_index());
851
852   // Now open a new empty tab. It should open at the end of the strip.
853   WebContents* fg_nonlink_contents = CreateWebContents();
854   insert_index = tabstrip.order_controller()->DetermineInsertionIndex(
855       content::PAGE_TRANSITION_AUTO_BOOKMARK, true);
856   EXPECT_EQ(tabstrip.count(), insert_index);
857   // We break the opener relationship...
858   tabstrip.InsertWebContentsAt(insert_index,
859                                fg_nonlink_contents,
860                                TabStripModel::ADD_NONE);
861   // Now select it, so that user_gesture == true causes the opener relationship
862   // to be forgotten...
863   tabstrip.ActivateTabAt(tabstrip.count() - 1, true);
864   EXPECT_EQ(tabstrip.count() - 1, tabstrip.active_index());
865   EXPECT_EQ(fg_nonlink_contents, tabstrip.GetActiveWebContents());
866
867   // Verify that all opener relationships are forgotten.
868   EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 2, false));
869   EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
870   EXPECT_EQ(-1, tabstrip.GetIndexOfNextWebContentsOpenedBy(opener, 3, false));
871   EXPECT_EQ(-1, tabstrip.GetIndexOfLastWebContentsOpenedBy(opener, 1));
872
873   tabstrip.CloseAllTabs();
874   EXPECT_TRUE(tabstrip.empty());
875 }
876
877 // Tests that selection is shifted to the correct tab when a tab is closed.
878 // If a tab is in the background when it is closed, the selection does not
879 // change.
880 // If a tab is in the foreground (selected),
881 //   If that tab does not have an opener, selection shifts to the right.
882 //   If the tab has an opener,
883 //     The next tab (scanning LTR) in the entire strip that has the same opener
884 //     is selected
885 //     If there are no other tabs that have the same opener,
886 //       The opener is selected
887 //
888 TEST_F(TabStripModelTest, TestSelectOnClose) {
889   TabStripDummyDelegate delegate;
890   TabStripModel tabstrip(&delegate, profile());
891   EXPECT_TRUE(tabstrip.empty());
892
893   WebContents* opener = CreateWebContents();
894   tabstrip.AppendWebContents(opener, true);
895
896   WebContents* contents1 = CreateWebContents();
897   WebContents* contents2 = CreateWebContents();
898   WebContents* contents3 = CreateWebContents();
899
900   // Note that we use Detach instead of Close throughout this test to avoid
901   // having to keep reconstructing these WebContentses.
902
903   // First test that closing tabs that are in the background doesn't adjust the
904   // current selection.
905   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
906   EXPECT_EQ(0, tabstrip.active_index());
907
908   tabstrip.DetachWebContentsAt(1);
909   EXPECT_EQ(0, tabstrip.active_index());
910
911   for (int i = tabstrip.count() - 1; i >= 1; --i)
912     tabstrip.DetachWebContentsAt(i);
913
914   // Now test that when a tab doesn't have an opener, selection shifts to the
915   // right when the tab is closed.
916   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
917   EXPECT_EQ(0, tabstrip.active_index());
918
919   tabstrip.ForgetAllOpeners();
920   tabstrip.ActivateTabAt(1, true);
921   EXPECT_EQ(1, tabstrip.active_index());
922   tabstrip.DetachWebContentsAt(1);
923   EXPECT_EQ(1, tabstrip.active_index());
924   tabstrip.DetachWebContentsAt(1);
925   EXPECT_EQ(1, tabstrip.active_index());
926   tabstrip.DetachWebContentsAt(1);
927   EXPECT_EQ(0, tabstrip.active_index());
928
929   for (int i = tabstrip.count() - 1; i >= 1; --i)
930     tabstrip.DetachWebContentsAt(i);
931
932   // Now test that when a tab does have an opener, it selects the next tab
933   // opened by the same opener scanning LTR when it is closed.
934   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
935   EXPECT_EQ(0, tabstrip.active_index());
936   tabstrip.ActivateTabAt(2, false);
937   EXPECT_EQ(2, tabstrip.active_index());
938   tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
939   EXPECT_EQ(2, tabstrip.active_index());
940   tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
941   EXPECT_EQ(1, tabstrip.active_index());
942   tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
943   EXPECT_EQ(0, tabstrip.active_index());
944   // Finally test that when a tab has no "siblings" that the opener is
945   // selected.
946   WebContents* other_contents = CreateWebContents();
947   tabstrip.InsertWebContentsAt(1, other_contents,
948                                TabStripModel::ADD_NONE);
949   EXPECT_EQ(2, tabstrip.count());
950   WebContents* opened_contents = CreateWebContents();
951   tabstrip.InsertWebContentsAt(2, opened_contents,
952                                TabStripModel::ADD_ACTIVE |
953                                TabStripModel::ADD_INHERIT_GROUP);
954   EXPECT_EQ(2, tabstrip.active_index());
955   tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
956   EXPECT_EQ(0, tabstrip.active_index());
957
958   tabstrip.CloseAllTabs();
959   EXPECT_TRUE(tabstrip.empty());
960 }
961
962 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
963 // CommandCloseTab.
964 TEST_F(TabStripModelTest, CommandCloseTab) {
965   TabStripDummyDelegate delegate;
966   TabStripModel tabstrip(&delegate, profile());
967   EXPECT_TRUE(tabstrip.empty());
968
969   // Make sure can_close is honored.
970   ASSERT_NO_FATAL_FAILURE(
971       PrepareTabstripForSelectionTest(&tabstrip, 1, 0, "0"));
972   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
973                   0, TabStripModel::CommandCloseTab));
974   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
975   ASSERT_TRUE(tabstrip.empty());
976
977   // Make sure close on a tab that is selected affects all the selected tabs.
978   ASSERT_NO_FATAL_FAILURE(
979       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
980   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
981                   0, TabStripModel::CommandCloseTab));
982   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
983   // Should have closed tabs 0 and 1.
984   EXPECT_EQ("2", GetTabStripStateString(tabstrip));
985
986   tabstrip.CloseAllTabs();
987   EXPECT_TRUE(tabstrip.empty());
988
989   // Select two tabs and make close on a tab that isn't selected doesn't affect
990   // selected tabs.
991   ASSERT_NO_FATAL_FAILURE(
992       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
993   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
994                   2, TabStripModel::CommandCloseTab));
995   tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
996   // Should have closed tab 2.
997   EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
998   tabstrip.CloseAllTabs();
999   EXPECT_TRUE(tabstrip.empty());
1000
1001   // Tests with 3 tabs, one pinned, two tab selected, one of which is pinned.
1002   ASSERT_NO_FATAL_FAILURE(
1003       PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1004   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1005                   0, TabStripModel::CommandCloseTab));
1006   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTab);
1007   // Should have closed tab 2.
1008   EXPECT_EQ("2", GetTabStripStateString(tabstrip));
1009   tabstrip.CloseAllTabs();
1010   EXPECT_TRUE(tabstrip.empty());
1011 }
1012
1013 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1014 // CommandCloseTabs.
1015 TEST_F(TabStripModelTest, CommandCloseOtherTabs) {
1016   TabStripDummyDelegate delegate;
1017   TabStripModel tabstrip(&delegate, profile());
1018   EXPECT_TRUE(tabstrip.empty());
1019
1020   // Create three tabs, select two tabs, CommandCloseOtherTabs should be enabled
1021   // and close two tabs.
1022   ASSERT_NO_FATAL_FAILURE(
1023       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1024   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1025                   0, TabStripModel::CommandCloseOtherTabs));
1026   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1027   EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1028   tabstrip.CloseAllTabs();
1029   EXPECT_TRUE(tabstrip.empty());
1030
1031   // Select two tabs, CommandCloseOtherTabs should be enabled and invoking it
1032   // with a non-selected index should close the two other tabs.
1033   ASSERT_NO_FATAL_FAILURE(
1034       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1"));
1035   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1036                   2, TabStripModel::CommandCloseOtherTabs));
1037   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseOtherTabs);
1038   EXPECT_EQ("0 1", GetTabStripStateString(tabstrip));
1039   tabstrip.CloseAllTabs();
1040   EXPECT_TRUE(tabstrip.empty());
1041
1042   // Select all, CommandCloseOtherTabs should not be enabled.
1043   ASSERT_NO_FATAL_FAILURE(
1044       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "0 1 2"));
1045   EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1046                   2, TabStripModel::CommandCloseOtherTabs));
1047   tabstrip.CloseAllTabs();
1048   EXPECT_TRUE(tabstrip.empty());
1049
1050   // Three tabs, pin one, select the two non-pinned.
1051   ASSERT_NO_FATAL_FAILURE(
1052       PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1 2"));
1053   EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1054                   1, TabStripModel::CommandCloseOtherTabs));
1055   // If we don't pass in the pinned index, the command should be enabled.
1056   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1057                   0, TabStripModel::CommandCloseOtherTabs));
1058   tabstrip.CloseAllTabs();
1059   EXPECT_TRUE(tabstrip.empty());
1060
1061   // 3 tabs, one pinned.
1062   ASSERT_NO_FATAL_FAILURE(
1063       PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "1"));
1064   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1065                   1, TabStripModel::CommandCloseOtherTabs));
1066   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1067                   0, TabStripModel::CommandCloseOtherTabs));
1068   tabstrip.ExecuteContextMenuCommand(1, TabStripModel::CommandCloseOtherTabs);
1069   // The pinned tab shouldn't be closed.
1070   EXPECT_EQ("0p 1", GetTabStripStateString(tabstrip));
1071   tabstrip.CloseAllTabs();
1072   EXPECT_TRUE(tabstrip.empty());
1073 }
1074
1075 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1076 // CommandCloseTabsToRight.
1077 TEST_F(TabStripModelTest, CommandCloseTabsToRight) {
1078   TabStripDummyDelegate delegate;
1079   TabStripModel tabstrip(&delegate, profile());
1080   EXPECT_TRUE(tabstrip.empty());
1081
1082   // Create three tabs, select last two tabs, CommandCloseTabsToRight should
1083   // only be enabled for the first tab.
1084   ASSERT_NO_FATAL_FAILURE(
1085       PrepareTabstripForSelectionTest(&tabstrip, 3, 0, "1 2"));
1086   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1087                   0, TabStripModel::CommandCloseTabsToRight));
1088   EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1089                    1, TabStripModel::CommandCloseTabsToRight));
1090   EXPECT_FALSE(tabstrip.IsContextMenuCommandEnabled(
1091                    2, TabStripModel::CommandCloseTabsToRight));
1092   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1093   EXPECT_EQ("0", GetTabStripStateString(tabstrip));
1094   tabstrip.CloseAllTabs();
1095   EXPECT_TRUE(tabstrip.empty());
1096 }
1097
1098 // Tests IsContextMenuCommandEnabled and ExecuteContextMenuCommand with
1099 // CommandTogglePinned.
1100 TEST_F(TabStripModelTest, CommandTogglePinned) {
1101   TabStripDummyDelegate delegate;
1102   TabStripModel tabstrip(&delegate, profile());
1103   EXPECT_TRUE(tabstrip.empty());
1104
1105   // Create three tabs with one pinned, pin the first two.
1106   ASSERT_NO_FATAL_FAILURE(
1107       PrepareTabstripForSelectionTest(&tabstrip, 3, 1, "0 1"));
1108   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1109                   0, TabStripModel::CommandTogglePinned));
1110   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1111                   1, TabStripModel::CommandTogglePinned));
1112   EXPECT_TRUE(tabstrip.IsContextMenuCommandEnabled(
1113                   2, TabStripModel::CommandTogglePinned));
1114   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1115   EXPECT_EQ("0p 1p 2", GetTabStripStateString(tabstrip));
1116
1117   // Execute CommandTogglePinned again, this should unpin.
1118   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandTogglePinned);
1119   EXPECT_EQ("0 1 2", GetTabStripStateString(tabstrip));
1120
1121   // Pin the last.
1122   tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandTogglePinned);
1123   EXPECT_EQ("2p 0 1", GetTabStripStateString(tabstrip));
1124
1125   tabstrip.CloseAllTabs();
1126   EXPECT_TRUE(tabstrip.empty());
1127 }
1128
1129 // Tests the following context menu commands:
1130 //  - Close Tab
1131 //  - Close Other Tabs
1132 //  - Close Tabs To Right
1133 TEST_F(TabStripModelTest, TestContextMenuCloseCommands) {
1134   TabStripDummyDelegate delegate;
1135   TabStripModel tabstrip(&delegate, profile());
1136   EXPECT_TRUE(tabstrip.empty());
1137
1138   WebContents* opener = CreateWebContents();
1139   tabstrip.AppendWebContents(opener, true);
1140
1141   WebContents* contents1 = CreateWebContents();
1142   WebContents* contents2 = CreateWebContents();
1143   WebContents* contents3 = CreateWebContents();
1144
1145   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1146   EXPECT_EQ(0, tabstrip.active_index());
1147
1148   tabstrip.ExecuteContextMenuCommand(2, TabStripModel::CommandCloseTab);
1149   EXPECT_EQ(3, tabstrip.count());
1150
1151   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandCloseTabsToRight);
1152   EXPECT_EQ(1, tabstrip.count());
1153   EXPECT_EQ(opener, tabstrip.GetActiveWebContents());
1154
1155   WebContents* dummy = CreateWebContents();
1156   tabstrip.AppendWebContents(dummy, false);
1157
1158   contents1 = CreateWebContents();
1159   contents2 = CreateWebContents();
1160   contents3 = CreateWebContents();
1161   InsertWebContentses(&tabstrip, contents1, contents2, contents3);
1162   EXPECT_EQ(5, tabstrip.count());
1163
1164   int dummy_index = tabstrip.count() - 1;
1165   tabstrip.ActivateTabAt(dummy_index, true);
1166   EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1167
1168   tabstrip.ExecuteContextMenuCommand(dummy_index,
1169                                      TabStripModel::CommandCloseOtherTabs);
1170   EXPECT_EQ(1, tabstrip.count());
1171   EXPECT_EQ(dummy, tabstrip.GetActiveWebContents());
1172
1173   tabstrip.CloseAllTabs();
1174   EXPECT_TRUE(tabstrip.empty());
1175 }
1176
1177 // Tests GetIndicesClosedByCommand.
1178 TEST_F(TabStripModelTest, GetIndicesClosedByCommand) {
1179   TabStripDummyDelegate delegate;
1180   TabStripModel tabstrip(&delegate, profile());
1181   EXPECT_TRUE(tabstrip.empty());
1182
1183   WebContents* contents1 = CreateWebContents();
1184   WebContents* contents2 = CreateWebContents();
1185   WebContents* contents3 = CreateWebContents();
1186   WebContents* contents4 = CreateWebContents();
1187   WebContents* contents5 = CreateWebContents();
1188
1189   tabstrip.AppendWebContents(contents1, true);
1190   tabstrip.AppendWebContents(contents2, true);
1191   tabstrip.AppendWebContents(contents3, true);
1192   tabstrip.AppendWebContents(contents4, true);
1193   tabstrip.AppendWebContents(contents5, true);
1194
1195   EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1196                 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1197   EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1198                 tabstrip, 1, TabStripModel::CommandCloseTabsToRight));
1199
1200   EXPECT_EQ("4 3 2 1", GetIndicesClosedByCommandAsString(
1201                 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1202   EXPECT_EQ("4 3 2 0", GetIndicesClosedByCommandAsString(
1203                 tabstrip, 1, TabStripModel::CommandCloseOtherTabs));
1204
1205   // Pin the first two tabs. Pinned tabs shouldn't be closed by the close other
1206   // commands.
1207   tabstrip.SetTabPinned(0, true);
1208   tabstrip.SetTabPinned(1, true);
1209
1210   EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1211                 tabstrip, 0, TabStripModel::CommandCloseTabsToRight));
1212   EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1213                 tabstrip, 2, TabStripModel::CommandCloseTabsToRight));
1214
1215   EXPECT_EQ("4 3 2", GetIndicesClosedByCommandAsString(
1216                 tabstrip, 0, TabStripModel::CommandCloseOtherTabs));
1217   EXPECT_EQ("4 3", GetIndicesClosedByCommandAsString(
1218                 tabstrip, 2, TabStripModel::CommandCloseOtherTabs));
1219
1220   tabstrip.CloseAllTabs();
1221   EXPECT_TRUE(tabstrip.empty());
1222 }
1223
1224 // Tests whether or not WebContentses are inserted in the correct position
1225 // using this "smart" function with a simulated middle click action on a series
1226 // of links on the home page.
1227 TEST_F(TabStripModelTest, AddWebContents_MiddleClickLinksAndClose) {
1228   TabStripDummyDelegate delegate;
1229   TabStripModel tabstrip(&delegate, profile());
1230   EXPECT_TRUE(tabstrip.empty());
1231
1232   // Open the Home Page.
1233   WebContents* homepage_contents = CreateWebContents();
1234   tabstrip.AddWebContents(
1235       homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1236       TabStripModel::ADD_ACTIVE);
1237
1238   // Open some other tab, by user typing.
1239   WebContents* typed_page_contents = CreateWebContents();
1240   tabstrip.AddWebContents(
1241       typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1242       TabStripModel::ADD_ACTIVE);
1243
1244   EXPECT_EQ(2, tabstrip.count());
1245
1246   // Re-select the home page.
1247   tabstrip.ActivateTabAt(0, true);
1248
1249   // Open a bunch of tabs by simulating middle clicking on links on the home
1250   // page.
1251   WebContents* middle_click_contents1 = CreateWebContents();
1252   tabstrip.AddWebContents(
1253       middle_click_contents1, -1, content::PAGE_TRANSITION_LINK,
1254       TabStripModel::ADD_NONE);
1255   WebContents* middle_click_contents2 = CreateWebContents();
1256   tabstrip.AddWebContents(
1257       middle_click_contents2, -1, content::PAGE_TRANSITION_LINK,
1258       TabStripModel::ADD_NONE);
1259   WebContents* middle_click_contents3 = CreateWebContents();
1260   tabstrip.AddWebContents(
1261       middle_click_contents3, -1, content::PAGE_TRANSITION_LINK,
1262       TabStripModel::ADD_NONE);
1263
1264   EXPECT_EQ(5, tabstrip.count());
1265
1266   EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1267   EXPECT_EQ(middle_click_contents1, tabstrip.GetWebContentsAt(1));
1268   EXPECT_EQ(middle_click_contents2, tabstrip.GetWebContentsAt(2));
1269   EXPECT_EQ(middle_click_contents3, tabstrip.GetWebContentsAt(3));
1270   EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1271
1272   // Now simulate selecting a tab in the middle of the group of tabs opened from
1273   // the home page and start closing them. Each WebContents in the group
1274   // should be closed, right to left. This test is constructed to start at the
1275   // middle WebContents in the group to make sure the cursor wraps around
1276   // to the first WebContents in the group before closing the opener or
1277   // any other WebContents.
1278   tabstrip.ActivateTabAt(2, true);
1279   tabstrip.CloseSelectedTabs();
1280   EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1281   tabstrip.CloseSelectedTabs();
1282   EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1283   tabstrip.CloseSelectedTabs();
1284   EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1285   tabstrip.CloseSelectedTabs();
1286   EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1287
1288   EXPECT_EQ(1, tabstrip.count());
1289
1290   tabstrip.CloseAllTabs();
1291   EXPECT_TRUE(tabstrip.empty());
1292 }
1293
1294 // Tests whether or not a WebContents created by a left click on a link
1295 // that opens a new tab is inserted correctly adjacent to the tab that spawned
1296 // it.
1297 TEST_F(TabStripModelTest, AddWebContents_LeftClickPopup) {
1298   TabStripDummyDelegate delegate;
1299   TabStripModel tabstrip(&delegate, profile());
1300   EXPECT_TRUE(tabstrip.empty());
1301
1302   // Open the Home Page.
1303   WebContents* homepage_contents = CreateWebContents();
1304   tabstrip.AddWebContents(
1305       homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1306       TabStripModel::ADD_ACTIVE);
1307
1308   // Open some other tab, by user typing.
1309   WebContents* typed_page_contents = CreateWebContents();
1310   tabstrip.AddWebContents(
1311       typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1312       TabStripModel::ADD_ACTIVE);
1313
1314   EXPECT_EQ(2, tabstrip.count());
1315
1316   // Re-select the home page.
1317   tabstrip.ActivateTabAt(0, true);
1318
1319   // Open a tab by simulating a left click on a link that opens in a new tab.
1320   WebContents* left_click_contents = CreateWebContents();
1321   tabstrip.AddWebContents(left_click_contents, -1,
1322                           content::PAGE_TRANSITION_LINK,
1323                           TabStripModel::ADD_ACTIVE);
1324
1325   // Verify the state meets our expectations.
1326   EXPECT_EQ(3, tabstrip.count());
1327   EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1328   EXPECT_EQ(left_click_contents, tabstrip.GetWebContentsAt(1));
1329   EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(2));
1330
1331   // The newly created tab should be selected.
1332   EXPECT_EQ(left_click_contents, tabstrip.GetActiveWebContents());
1333
1334   // After closing the selected tab, the selection should move to the left, to
1335   // the opener.
1336   tabstrip.CloseSelectedTabs();
1337   EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1338
1339   EXPECT_EQ(2, tabstrip.count());
1340
1341   tabstrip.CloseAllTabs();
1342   EXPECT_TRUE(tabstrip.empty());
1343 }
1344
1345 // Tests whether or not new tabs that should split context (typed pages,
1346 // generated urls, also blank tabs) open at the end of the tabstrip instead of
1347 // in the middle.
1348 TEST_F(TabStripModelTest, AddWebContents_CreateNewBlankTab) {
1349   TabStripDummyDelegate delegate;
1350   TabStripModel tabstrip(&delegate, profile());
1351   EXPECT_TRUE(tabstrip.empty());
1352
1353   // Open the Home Page.
1354   WebContents* homepage_contents = CreateWebContents();
1355   tabstrip.AddWebContents(
1356       homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1357       TabStripModel::ADD_ACTIVE);
1358
1359   // Open some other tab, by user typing.
1360   WebContents* typed_page_contents = CreateWebContents();
1361   tabstrip.AddWebContents(
1362       typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1363       TabStripModel::ADD_ACTIVE);
1364
1365   EXPECT_EQ(2, tabstrip.count());
1366
1367   // Re-select the home page.
1368   tabstrip.ActivateTabAt(0, true);
1369
1370   // Open a new blank tab in the foreground.
1371   WebContents* new_blank_contents = CreateWebContents();
1372   tabstrip.AddWebContents(new_blank_contents, -1,
1373                           content::PAGE_TRANSITION_TYPED,
1374                           TabStripModel::ADD_ACTIVE);
1375
1376   // Verify the state of the tabstrip.
1377   EXPECT_EQ(3, tabstrip.count());
1378   EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1379   EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1380   EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1381
1382   // Now open a couple more blank tabs in the background.
1383   WebContents* background_blank_contents1 = CreateWebContents();
1384   tabstrip.AddWebContents(
1385       background_blank_contents1, -1, content::PAGE_TRANSITION_TYPED,
1386       TabStripModel::ADD_NONE);
1387   WebContents* background_blank_contents2 = CreateWebContents();
1388   tabstrip.AddWebContents(
1389       background_blank_contents2, -1, content::PAGE_TRANSITION_GENERATED,
1390       TabStripModel::ADD_NONE);
1391   EXPECT_EQ(5, tabstrip.count());
1392   EXPECT_EQ(homepage_contents, tabstrip.GetWebContentsAt(0));
1393   EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(1));
1394   EXPECT_EQ(new_blank_contents, tabstrip.GetWebContentsAt(2));
1395   EXPECT_EQ(background_blank_contents1, tabstrip.GetWebContentsAt(3));
1396   EXPECT_EQ(background_blank_contents2, tabstrip.GetWebContentsAt(4));
1397
1398   tabstrip.CloseAllTabs();
1399   EXPECT_TRUE(tabstrip.empty());
1400 }
1401
1402 // Tests whether opener state is correctly forgotten when the user switches
1403 // context.
1404 TEST_F(TabStripModelTest, AddWebContents_ForgetOpeners) {
1405   TabStripDummyDelegate delegate;
1406   TabStripModel tabstrip(&delegate, profile());
1407   EXPECT_TRUE(tabstrip.empty());
1408
1409   // Open the Home Page
1410   WebContents* homepage_contents = CreateWebContents();
1411   tabstrip.AddWebContents(
1412       homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1413       TabStripModel::ADD_ACTIVE);
1414
1415   // Open some other tab, by user typing.
1416   WebContents* typed_page_contents = CreateWebContents();
1417   tabstrip.AddWebContents(
1418       typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1419       TabStripModel::ADD_ACTIVE);
1420
1421   EXPECT_EQ(2, tabstrip.count());
1422
1423   // Re-select the home page.
1424   tabstrip.ActivateTabAt(0, true);
1425
1426   // Open a bunch of tabs by simulating middle clicking on links on the home
1427   // page.
1428   WebContents* middle_click_contents1 = CreateWebContents();
1429   tabstrip.AddWebContents(
1430       middle_click_contents1, -1, content::PAGE_TRANSITION_LINK,
1431       TabStripModel::ADD_NONE);
1432   WebContents* middle_click_contents2 = CreateWebContents();
1433   tabstrip.AddWebContents(
1434       middle_click_contents2, -1, content::PAGE_TRANSITION_LINK,
1435       TabStripModel::ADD_NONE);
1436   WebContents* middle_click_contents3 = CreateWebContents();
1437   tabstrip.AddWebContents(
1438       middle_click_contents3, -1, content::PAGE_TRANSITION_LINK,
1439       TabStripModel::ADD_NONE);
1440
1441   // Break out of the context by selecting a tab in a different context.
1442   EXPECT_EQ(typed_page_contents, tabstrip.GetWebContentsAt(4));
1443   tabstrip.SelectLastTab();
1444   EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1445
1446   // Step back into the context by selecting a tab inside it.
1447   tabstrip.ActivateTabAt(2, true);
1448   EXPECT_EQ(middle_click_contents2, tabstrip.GetActiveWebContents());
1449
1450   // Now test that closing tabs selects to the right until there are no more,
1451   // then to the left, as if there were no context (context has been
1452   // successfully forgotten).
1453   tabstrip.CloseSelectedTabs();
1454   EXPECT_EQ(middle_click_contents3, tabstrip.GetActiveWebContents());
1455   tabstrip.CloseSelectedTabs();
1456   EXPECT_EQ(typed_page_contents, tabstrip.GetActiveWebContents());
1457   tabstrip.CloseSelectedTabs();
1458   EXPECT_EQ(middle_click_contents1, tabstrip.GetActiveWebContents());
1459   tabstrip.CloseSelectedTabs();
1460   EXPECT_EQ(homepage_contents, tabstrip.GetActiveWebContents());
1461
1462   EXPECT_EQ(1, tabstrip.count());
1463
1464   tabstrip.CloseAllTabs();
1465   EXPECT_TRUE(tabstrip.empty());
1466 }
1467
1468 // Added for http://b/issue?id=958960
1469 TEST_F(TabStripModelTest, AppendContentsReselectionTest) {
1470   TabStripDummyDelegate delegate;
1471   TabStripModel tabstrip(&delegate, profile());
1472   EXPECT_TRUE(tabstrip.empty());
1473
1474   // Open the Home Page.
1475   WebContents* homepage_contents = CreateWebContents();
1476   tabstrip.AddWebContents(
1477       homepage_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1478       TabStripModel::ADD_ACTIVE);
1479
1480   // Open some other tab, by user typing.
1481   WebContents* typed_page_contents = CreateWebContents();
1482   tabstrip.AddWebContents(
1483       typed_page_contents, -1, content::PAGE_TRANSITION_TYPED,
1484       TabStripModel::ADD_NONE);
1485
1486   // The selected tab should still be the first.
1487   EXPECT_EQ(0, tabstrip.active_index());
1488
1489   // Now simulate a link click that opens a new tab (by virtue of target=_blank)
1490   // and make sure the correct tab gets selected when the new tab is closed.
1491   WebContents* target_blank = CreateWebContents();
1492   tabstrip.AppendWebContents(target_blank, true);
1493   EXPECT_EQ(2, tabstrip.active_index());
1494   tabstrip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1495   EXPECT_EQ(0, tabstrip.active_index());
1496
1497   // Clean up after ourselves.
1498   tabstrip.CloseAllTabs();
1499 }
1500
1501 // Added for http://b/issue?id=1027661
1502 TEST_F(TabStripModelTest, ReselectionConsidersChildrenTest) {
1503   TabStripDummyDelegate delegate;
1504   TabStripModel strip(&delegate, profile());
1505
1506   // Open page A
1507   WebContents* page_a_contents = CreateWebContents();
1508   strip.AddWebContents(
1509       page_a_contents, -1, content::PAGE_TRANSITION_AUTO_BOOKMARK,
1510       TabStripModel::ADD_ACTIVE);
1511
1512   // Simulate middle click to open page A.A and A.B
1513   WebContents* page_a_a_contents = CreateWebContents();
1514   strip.AddWebContents(page_a_a_contents, -1, content::PAGE_TRANSITION_LINK,
1515                        TabStripModel::ADD_NONE);
1516   WebContents* page_a_b_contents = CreateWebContents();
1517   strip.AddWebContents(page_a_b_contents, -1, content::PAGE_TRANSITION_LINK,
1518                        TabStripModel::ADD_NONE);
1519
1520   // Select page A.A
1521   strip.ActivateTabAt(1, true);
1522   EXPECT_EQ(page_a_a_contents, strip.GetActiveWebContents());
1523
1524   // Simulate a middle click to open page A.A.A
1525   WebContents* page_a_a_a_contents = CreateWebContents();
1526   strip.AddWebContents(page_a_a_a_contents, -1, content::PAGE_TRANSITION_LINK,
1527                        TabStripModel::ADD_NONE);
1528
1529   EXPECT_EQ(page_a_a_a_contents, strip.GetWebContentsAt(2));
1530
1531   // Close page A.A
1532   strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1533
1534   // Page A.A.A should be selected, NOT A.B
1535   EXPECT_EQ(page_a_a_a_contents, strip.GetActiveWebContents());
1536
1537   // Close page A.A.A
1538   strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1539
1540   // Page A.B should be selected
1541   EXPECT_EQ(page_a_b_contents, strip.GetActiveWebContents());
1542
1543   // Close page A.B
1544   strip.CloseWebContentsAt(strip.active_index(), TabStripModel::CLOSE_NONE);
1545
1546   // Page A should be selected
1547   EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
1548
1549   // Clean up.
1550   strip.CloseAllTabs();
1551 }
1552
1553 TEST_F(TabStripModelTest, AddWebContents_NewTabAtEndOfStripInheritsGroup) {
1554   TabStripDummyDelegate delegate;
1555   TabStripModel strip(&delegate, profile());
1556
1557   // Open page A
1558   WebContents* page_a_contents = CreateWebContents();
1559   strip.AddWebContents(page_a_contents, -1,
1560                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1561                        TabStripModel::ADD_ACTIVE);
1562
1563   // Open pages B, C and D in the background from links on page A...
1564   WebContents* page_b_contents = CreateWebContents();
1565   WebContents* page_c_contents = CreateWebContents();
1566   WebContents* page_d_contents = CreateWebContents();
1567   strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1568                        TabStripModel::ADD_NONE);
1569   strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1570                        TabStripModel::ADD_NONE);
1571   strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1572                        TabStripModel::ADD_NONE);
1573
1574   // Switch to page B's tab.
1575   strip.ActivateTabAt(1, true);
1576
1577   // Open a New Tab at the end of the strip (simulate Ctrl+T)
1578   WebContents* new_contents = CreateWebContents();
1579   strip.AddWebContents(new_contents, -1, content::PAGE_TRANSITION_TYPED,
1580                        TabStripModel::ADD_ACTIVE);
1581
1582   EXPECT_EQ(4, strip.GetIndexOfWebContents(new_contents));
1583   EXPECT_EQ(4, strip.active_index());
1584
1585   // Close the New Tab that was just opened. We should be returned to page B's
1586   // Tab...
1587   strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1588
1589   EXPECT_EQ(1, strip.active_index());
1590
1591   // Open a non-New Tab tab at the end of the strip, with a TYPED transition.
1592   // This is like typing a URL in the address bar and pressing Alt+Enter. The
1593   // behavior should be the same as above.
1594   WebContents* page_e_contents = CreateWebContents();
1595   strip.AddWebContents(page_e_contents, -1, content::PAGE_TRANSITION_TYPED,
1596                        TabStripModel::ADD_ACTIVE);
1597
1598   EXPECT_EQ(4, strip.GetIndexOfWebContents(page_e_contents));
1599   EXPECT_EQ(4, strip.active_index());
1600
1601   // Close the Tab. Selection should shift back to page B's Tab.
1602   strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1603
1604   EXPECT_EQ(1, strip.active_index());
1605
1606   // Open a non-New Tab tab at the end of the strip, with some other
1607   // transition. This is like right clicking on a bookmark and choosing "Open
1608   // in New Tab". No opener relationship should be preserved between this Tab
1609   // and the one that was active when the gesture was performed.
1610   WebContents* page_f_contents = CreateWebContents();
1611   strip.AddWebContents(page_f_contents, -1,
1612                        content::PAGE_TRANSITION_AUTO_BOOKMARK,
1613                        TabStripModel::ADD_ACTIVE);
1614
1615   EXPECT_EQ(4, strip.GetIndexOfWebContents(page_f_contents));
1616   EXPECT_EQ(4, strip.active_index());
1617
1618   // Close the Tab. The next-adjacent should be selected.
1619   strip.CloseWebContentsAt(4, TabStripModel::CLOSE_NONE);
1620
1621   EXPECT_EQ(3, strip.active_index());
1622
1623   // Clean up.
1624   strip.CloseAllTabs();
1625 }
1626
1627 // A test of navigations in a tab that is part of a group of opened from some
1628 // parent tab. If the navigations are link clicks, the group relationship of
1629 // the tab to its parent are preserved. If they are of any other type, they are
1630 // not preserved.
1631 TEST_F(TabStripModelTest, NavigationForgetsOpeners) {
1632   TabStripDummyDelegate delegate;
1633   TabStripModel strip(&delegate, profile());
1634
1635   // Open page A
1636   WebContents* page_a_contents = CreateWebContents();
1637   strip.AddWebContents(page_a_contents, -1,
1638                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1639                        TabStripModel::ADD_ACTIVE);
1640
1641   // Open pages B, C and D in the background from links on page A...
1642   WebContents* page_b_contents = CreateWebContents();
1643   WebContents* page_c_contents = CreateWebContents();
1644   WebContents* page_d_contents = CreateWebContents();
1645   strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1646                        TabStripModel::ADD_NONE);
1647   strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1648                        TabStripModel::ADD_NONE);
1649   strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1650                        TabStripModel::ADD_NONE);
1651
1652   // Open page E in a different opener group from page A.
1653   WebContents* page_e_contents = CreateWebContents();
1654   strip.AddWebContents(page_e_contents, -1,
1655                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1656                        TabStripModel::ADD_NONE);
1657
1658   // Tell the TabStripModel that we are navigating page D via a link click.
1659   strip.ActivateTabAt(3, true);
1660   strip.TabNavigating(page_d_contents, content::PAGE_TRANSITION_LINK);
1661
1662   // Close page D, page C should be selected. (part of same group).
1663   strip.CloseWebContentsAt(3, TabStripModel::CLOSE_NONE);
1664   EXPECT_EQ(2, strip.active_index());
1665
1666   // Tell the TabStripModel that we are navigating in page C via a bookmark.
1667   strip.TabNavigating(page_c_contents, content::PAGE_TRANSITION_AUTO_BOOKMARK);
1668
1669   // Close page C, page E should be selected. (C is no longer part of the
1670   // A-B-C-D group, selection moves to the right).
1671   strip.CloseWebContentsAt(2, TabStripModel::CLOSE_NONE);
1672   EXPECT_EQ(page_e_contents, strip.GetWebContentsAt(strip.active_index()));
1673
1674   strip.CloseAllTabs();
1675 }
1676
1677 // A test that the forgetting behavior tested in NavigationForgetsOpeners above
1678 // doesn't cause the opener relationship for a New Tab opened at the end of the
1679 // TabStrip to be reset (Test 1 below), unless another any other tab is
1680 // selected (Test 2 below).
1681 TEST_F(TabStripModelTest, NavigationForgettingDoesntAffectNewTab) {
1682   TabStripDummyDelegate delegate;
1683   TabStripModel strip(&delegate, profile());
1684
1685   // Open a tab and several tabs from it, then select one of the tabs that was
1686   // opened.
1687   WebContents* page_a_contents = CreateWebContents();
1688   strip.AddWebContents(page_a_contents, -1,
1689                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
1690                        TabStripModel::ADD_ACTIVE);
1691
1692   WebContents* page_b_contents = CreateWebContents();
1693   WebContents* page_c_contents = CreateWebContents();
1694   WebContents* page_d_contents = CreateWebContents();
1695   strip.AddWebContents(page_b_contents, -1, content::PAGE_TRANSITION_LINK,
1696                        TabStripModel::ADD_NONE);
1697   strip.AddWebContents(page_c_contents, -1, content::PAGE_TRANSITION_LINK,
1698                        TabStripModel::ADD_NONE);
1699   strip.AddWebContents(page_d_contents, -1, content::PAGE_TRANSITION_LINK,
1700                        TabStripModel::ADD_NONE);
1701
1702   strip.ActivateTabAt(2, true);
1703
1704   // TEST 1: If the user is in a group of tabs and opens a new tab at the end
1705   // of the strip, closing that new tab will select the tab that they were
1706   // last on.
1707
1708   // Now simulate opening a new tab at the end of the TabStrip.
1709   WebContents* new_contents1 = CreateWebContents();
1710   strip.AddWebContents(new_contents1, -1, content::PAGE_TRANSITION_TYPED,
1711                        TabStripModel::ADD_ACTIVE);
1712
1713   // At this point, if we close this tab the last selected one should be
1714   // re-selected.
1715   strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1716   EXPECT_EQ(page_c_contents, strip.GetWebContentsAt(strip.active_index()));
1717
1718   // TEST 2: If the user is in a group of tabs and opens a new tab at the end
1719   // of the strip, selecting any other tab in the strip will cause that new
1720   // tab's opener relationship to be forgotten.
1721
1722   // Open a new tab again.
1723   WebContents* new_contents2 = CreateWebContents();
1724   strip.AddWebContents(new_contents2, -1, content::PAGE_TRANSITION_TYPED,
1725                        TabStripModel::ADD_ACTIVE);
1726
1727   // Now select the first tab.
1728   strip.ActivateTabAt(0, true);
1729
1730   // Now select the last tab.
1731   strip.ActivateTabAt(strip.count() - 1, true);
1732
1733   // Now close the last tab. The next adjacent should be selected.
1734   strip.CloseWebContentsAt(strip.count() - 1, TabStripModel::CLOSE_NONE);
1735   EXPECT_EQ(page_d_contents, strip.GetWebContentsAt(strip.active_index()));
1736
1737   strip.CloseAllTabs();
1738 }
1739
1740 // This fails on Linux when run with the rest of unit_tests (crbug.com/302156)
1741 // and fails consistently on Mac and Windows.
1742 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
1743 #define MAYBE_FastShutdown \
1744     DISABLED_FastShutdown
1745 #else
1746 #define MAYBE_FastShutdown \
1747     FastShutdown
1748 #endif
1749 // Tests that fast shutdown is attempted appropriately.
1750 TEST_F(TabStripModelTest, MAYBE_FastShutdown) {
1751   TabStripDummyDelegate delegate;
1752   TabStripModel tabstrip(&delegate, profile());
1753   MockTabStripModelObserver observer(&tabstrip);
1754   tabstrip.AddObserver(&observer);
1755
1756   EXPECT_TRUE(tabstrip.empty());
1757
1758   // Make sure fast shutdown is attempted when tabs that share a RPH are shut
1759   // down.
1760   {
1761     WebContents* contents1 = CreateWebContents();
1762     WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1763
1764     SetID(contents1, 1);
1765     SetID(contents2, 2);
1766
1767     tabstrip.AppendWebContents(contents1, true);
1768     tabstrip.AppendWebContents(contents2, true);
1769
1770     // Turn on the fake unload listener so the tabs don't actually get shut
1771     // down when we call CloseAllTabs()---we need to be able to check that
1772     // fast shutdown was attempted.
1773     delegate.set_run_unload_listener(true);
1774     tabstrip.CloseAllTabs();
1775     // On a mock RPH this checks whether we *attempted* fast shutdown.
1776     // A real RPH would reject our attempt since there is an unload handler.
1777     EXPECT_TRUE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1778     EXPECT_EQ(2, tabstrip.count());
1779
1780     delegate.set_run_unload_listener(false);
1781     tabstrip.CloseAllTabs();
1782     EXPECT_TRUE(tabstrip.empty());
1783   }
1784
1785   // Make sure fast shutdown is not attempted when only some tabs that share a
1786   // RPH are shut down.
1787   {
1788     WebContents* contents1 = CreateWebContents();
1789     WebContents* contents2 = CreateWebContentsWithSharedRPH(contents1);
1790
1791     SetID(contents1, 1);
1792     SetID(contents2, 2);
1793
1794     tabstrip.AppendWebContents(contents1, true);
1795     tabstrip.AppendWebContents(contents2, true);
1796
1797     tabstrip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
1798     EXPECT_FALSE(contents1->GetRenderProcessHost()->FastShutdownStarted());
1799     EXPECT_EQ(1, tabstrip.count());
1800
1801     tabstrip.CloseAllTabs();
1802     EXPECT_TRUE(tabstrip.empty());
1803   }
1804 }
1805
1806 // Tests various permutations of apps.
1807 TEST_F(TabStripModelTest, Apps) {
1808   TabStripDummyDelegate delegate;
1809   TabStripModel tabstrip(&delegate, profile());
1810   MockTabStripModelObserver observer(&tabstrip);
1811   tabstrip.AddObserver(&observer);
1812
1813   EXPECT_TRUE(tabstrip.empty());
1814
1815   typedef MockTabStripModelObserver::State State;
1816
1817 #if defined(OS_WIN)
1818   base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
1819 #elif defined(OS_POSIX)
1820   base::FilePath path(FILE_PATH_LITERAL("/foo"));
1821 #endif
1822
1823   base::DictionaryValue manifest;
1824   manifest.SetString("name", "hi!");
1825   manifest.SetString("version", "1");
1826   manifest.SetString("app.launch.web_url", "http://www.google.com");
1827   std::string error;
1828   scoped_refptr<Extension> extension_app(
1829       Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
1830                         manifest, Extension::NO_FLAGS, &error));
1831   WebContents* contents1 = CreateWebContents();
1832   extensions::TabHelper::CreateForWebContents(contents1);
1833   extensions::TabHelper::FromWebContents(contents1)
1834       ->SetExtensionApp(extension_app.get());
1835   WebContents* contents2 = CreateWebContents();
1836   extensions::TabHelper::CreateForWebContents(contents2);
1837   extensions::TabHelper::FromWebContents(contents2)
1838       ->SetExtensionApp(extension_app.get());
1839   WebContents* contents3 = CreateWebContents();
1840
1841   SetID(contents1, 1);
1842   SetID(contents2, 2);
1843   SetID(contents3, 3);
1844
1845   // Note! The ordering of these tests is important, each subsequent test
1846   // builds on the state established in the previous. This is important if you
1847   // ever insert tests rather than append.
1848
1849   // Initial state, tab3 only and selected.
1850   tabstrip.AppendWebContents(contents3, true);
1851
1852   observer.ClearStates();
1853
1854   // Attempt to insert tab1 (an app tab) at position 1. This isn't a legal
1855   // position and tab1 should end up at position 0.
1856   {
1857     tabstrip.InsertWebContentsAt(1, contents1, TabStripModel::ADD_NONE);
1858
1859     ASSERT_EQ(1, observer.GetStateCount());
1860     State state(contents1, 0, MockTabStripModelObserver::INSERT);
1861     EXPECT_TRUE(observer.StateEquals(0, state));
1862
1863     // And verify the state.
1864     EXPECT_EQ("1ap 3", GetTabStripStateString(tabstrip));
1865
1866     observer.ClearStates();
1867   }
1868
1869   // Insert tab 2 at position 1.
1870   {
1871     tabstrip.InsertWebContentsAt(1, contents2, TabStripModel::ADD_NONE);
1872
1873     ASSERT_EQ(1, observer.GetStateCount());
1874     State state(contents2, 1, MockTabStripModelObserver::INSERT);
1875     EXPECT_TRUE(observer.StateEquals(0, state));
1876
1877     // And verify the state.
1878     EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1879
1880     observer.ClearStates();
1881   }
1882
1883   // Try to move tab 3 to position 0. This isn't legal and should be ignored.
1884   {
1885     tabstrip.MoveWebContentsAt(2, 0, false);
1886
1887     ASSERT_EQ(0, observer.GetStateCount());
1888
1889     // And verify the state didn't change.
1890     EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1891
1892     observer.ClearStates();
1893   }
1894
1895   // Try to move tab 0 to position 3. This isn't legal and should be ignored.
1896   {
1897     tabstrip.MoveWebContentsAt(0, 2, false);
1898
1899     ASSERT_EQ(0, observer.GetStateCount());
1900
1901     // And verify the state didn't change.
1902     EXPECT_EQ("1ap 2ap 3", GetTabStripStateString(tabstrip));
1903
1904     observer.ClearStates();
1905   }
1906
1907   // Try to move tab 0 to position 1. This is a legal move.
1908   {
1909     tabstrip.MoveWebContentsAt(0, 1, false);
1910
1911     ASSERT_EQ(1, observer.GetStateCount());
1912     State state(contents1, 1, MockTabStripModelObserver::MOVE);
1913     state.src_index = 0;
1914     EXPECT_TRUE(observer.StateEquals(0, state));
1915
1916     // And verify the state didn't change.
1917     EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1918
1919     observer.ClearStates();
1920   }
1921
1922   // Remove tab3 and insert at position 0. It should be forced to position 2.
1923   {
1924     tabstrip.DetachWebContentsAt(2);
1925     observer.ClearStates();
1926
1927     tabstrip.InsertWebContentsAt(0, contents3, TabStripModel::ADD_NONE);
1928
1929     ASSERT_EQ(1, observer.GetStateCount());
1930     State state(contents3, 2, MockTabStripModelObserver::INSERT);
1931     EXPECT_TRUE(observer.StateEquals(0, state));
1932
1933     // And verify the state didn't change.
1934     EXPECT_EQ("2ap 1ap 3", GetTabStripStateString(tabstrip));
1935
1936     observer.ClearStates();
1937   }
1938
1939   tabstrip.CloseAllTabs();
1940 }
1941
1942 // Tests various permutations of pinning tabs.
1943 TEST_F(TabStripModelTest, Pinning) {
1944   TabStripDummyDelegate delegate;
1945   TabStripModel tabstrip(&delegate, profile());
1946   MockTabStripModelObserver observer(&tabstrip);
1947   tabstrip.AddObserver(&observer);
1948
1949   EXPECT_TRUE(tabstrip.empty());
1950
1951   typedef MockTabStripModelObserver::State State;
1952
1953   WebContents* contents1 = CreateWebContents();
1954   WebContents* contents2 = CreateWebContents();
1955   WebContents* contents3 = CreateWebContents();
1956
1957   SetID(contents1, 1);
1958   SetID(contents2, 2);
1959   SetID(contents3, 3);
1960
1961   // Note! The ordering of these tests is important, each subsequent test
1962   // builds on the state established in the previous. This is important if you
1963   // ever insert tests rather than append.
1964
1965   // Initial state, three tabs, first selected.
1966   tabstrip.AppendWebContents(contents1, true);
1967   tabstrip.AppendWebContents(contents2, false);
1968   tabstrip.AppendWebContents(contents3, false);
1969
1970   observer.ClearStates();
1971
1972   // Pin the first tab, this shouldn't visually reorder anything.
1973   {
1974     tabstrip.SetTabPinned(0, true);
1975
1976     // As the order didn't change, we should get a pinned notification.
1977     ASSERT_EQ(1, observer.GetStateCount());
1978     State state(contents1, 0, MockTabStripModelObserver::PINNED);
1979     EXPECT_TRUE(observer.StateEquals(0, state));
1980
1981     // And verify the state.
1982     EXPECT_EQ("1p 2 3", GetTabStripStateString(tabstrip));
1983
1984     observer.ClearStates();
1985   }
1986
1987   // Unpin the first tab.
1988   {
1989     tabstrip.SetTabPinned(0, false);
1990
1991     // As the order didn't change, we should get a pinned notification.
1992     ASSERT_EQ(1, observer.GetStateCount());
1993     State state(contents1, 0, MockTabStripModelObserver::PINNED);
1994     EXPECT_TRUE(observer.StateEquals(0, state));
1995
1996     // And verify the state.
1997     EXPECT_EQ("1 2 3", GetTabStripStateString(tabstrip));
1998
1999     observer.ClearStates();
2000   }
2001
2002   // Pin the 3rd tab, which should move it to the front.
2003   {
2004     tabstrip.SetTabPinned(2, true);
2005
2006     // The pinning should have resulted in a move and a pinned notification.
2007     ASSERT_EQ(2, observer.GetStateCount());
2008     State state(contents3, 0, MockTabStripModelObserver::MOVE);
2009     state.src_index = 2;
2010     EXPECT_TRUE(observer.StateEquals(0, state));
2011
2012     state = State(contents3, 0, MockTabStripModelObserver::PINNED);
2013     EXPECT_TRUE(observer.StateEquals(1, state));
2014
2015     // And verify the state.
2016     EXPECT_EQ("3p 1 2", GetTabStripStateString(tabstrip));
2017
2018     observer.ClearStates();
2019   }
2020
2021   // Pin the tab "1", which shouldn't move anything.
2022   {
2023     tabstrip.SetTabPinned(1, true);
2024
2025     // As the order didn't change, we should get a pinned notification.
2026     ASSERT_EQ(1, observer.GetStateCount());
2027     State state(contents1, 1, MockTabStripModelObserver::PINNED);
2028     EXPECT_TRUE(observer.StateEquals(0, state));
2029
2030     // And verify the state.
2031     EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2032
2033     observer.ClearStates();
2034   }
2035
2036   // Try to move tab "2" to the front, it should be ignored.
2037   {
2038     tabstrip.MoveWebContentsAt(2, 0, false);
2039
2040     // As the order didn't change, we should get a pinned notification.
2041     ASSERT_EQ(0, observer.GetStateCount());
2042
2043     // And verify the state.
2044     EXPECT_EQ("3p 1p 2", GetTabStripStateString(tabstrip));
2045
2046     observer.ClearStates();
2047   }
2048
2049   // Unpin tab "3", which implicitly moves it to the end.
2050   {
2051     tabstrip.SetTabPinned(0, false);
2052
2053     ASSERT_EQ(2, observer.GetStateCount());
2054     State state(contents3, 1, MockTabStripModelObserver::MOVE);
2055     state.src_index = 0;
2056     EXPECT_TRUE(observer.StateEquals(0, state));
2057
2058     state = State(contents3, 1, MockTabStripModelObserver::PINNED);
2059     EXPECT_TRUE(observer.StateEquals(1, state));
2060
2061     // And verify the state.
2062     EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2063
2064     observer.ClearStates();
2065   }
2066
2067   // Unpin tab "3", nothing should happen.
2068   {
2069     tabstrip.SetTabPinned(1, false);
2070
2071     ASSERT_EQ(0, observer.GetStateCount());
2072
2073     EXPECT_EQ("1p 3 2", GetTabStripStateString(tabstrip));
2074
2075     observer.ClearStates();
2076   }
2077
2078   // Pin "3" and "1".
2079   {
2080     tabstrip.SetTabPinned(0, true);
2081     tabstrip.SetTabPinned(1, true);
2082
2083     EXPECT_EQ("1p 3p 2", GetTabStripStateString(tabstrip));
2084
2085     observer.ClearStates();
2086   }
2087
2088   WebContents* contents4 = CreateWebContents();
2089   SetID(contents4, 4);
2090
2091   // Insert "4" between "1" and "3". As "1" and "4" are pinned, "4" should end
2092   // up after them.
2093   {
2094     tabstrip.InsertWebContentsAt(1, contents4, TabStripModel::ADD_NONE);
2095
2096     ASSERT_EQ(1, observer.GetStateCount());
2097     State state(contents4, 2, MockTabStripModelObserver::INSERT);
2098     EXPECT_TRUE(observer.StateEquals(0, state));
2099
2100     EXPECT_EQ("1p 3p 4 2", GetTabStripStateString(tabstrip));
2101   }
2102
2103   tabstrip.CloseAllTabs();
2104 }
2105
2106 // Makes sure the TabStripModel calls the right observer methods during a
2107 // replace.
2108 TEST_F(TabStripModelTest, ReplaceSendsSelected) {
2109   typedef MockTabStripModelObserver::State State;
2110
2111   TabStripDummyDelegate delegate;
2112   TabStripModel strip(&delegate, profile());
2113
2114   WebContents* first_contents = CreateWebContents();
2115   strip.AddWebContents(first_contents, -1, content::PAGE_TRANSITION_TYPED,
2116                        TabStripModel::ADD_ACTIVE);
2117
2118   MockTabStripModelObserver tabstrip_observer(&strip);
2119   strip.AddObserver(&tabstrip_observer);
2120
2121   WebContents* new_contents = CreateWebContents();
2122   delete strip.ReplaceWebContentsAt(0, new_contents);
2123
2124   ASSERT_EQ(2, tabstrip_observer.GetStateCount());
2125
2126   // First event should be for replaced.
2127   State state(new_contents, 0, MockTabStripModelObserver::REPLACED);
2128   state.src_contents = first_contents;
2129   EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2130
2131   // And the second for selected.
2132   state = State(new_contents, 0, MockTabStripModelObserver::ACTIVATE);
2133   state.src_contents = first_contents;
2134   state.change_reason = TabStripModelObserver::CHANGE_REASON_REPLACED;
2135   EXPECT_TRUE(tabstrip_observer.StateEquals(1, state));
2136
2137   // Now add another tab and replace it, making sure we don't get a selected
2138   // event this time.
2139   WebContents* third_contents = CreateWebContents();
2140   strip.AddWebContents(third_contents, 1, content::PAGE_TRANSITION_TYPED,
2141                        TabStripModel::ADD_NONE);
2142
2143   tabstrip_observer.ClearStates();
2144
2145   // And replace it.
2146   new_contents = CreateWebContents();
2147   delete strip.ReplaceWebContentsAt(1, new_contents);
2148
2149   ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2150
2151   state = State(new_contents, 1, MockTabStripModelObserver::REPLACED);
2152   state.src_contents = third_contents;
2153   EXPECT_TRUE(tabstrip_observer.StateEquals(0, state));
2154
2155   strip.CloseAllTabs();
2156 }
2157
2158 // Ensures discarding tabs leaves TabStripModel in a good state.
2159 TEST_F(TabStripModelTest, DiscardWebContentsAt) {
2160   typedef MockTabStripModelObserver::State State;
2161
2162   TabStripDummyDelegate delegate;
2163   TabStripModel tabstrip(&delegate, profile());
2164
2165   // Fill it with some tabs.
2166   WebContents* contents1 = CreateWebContents();
2167   tabstrip.AppendWebContents(contents1, true);
2168   WebContents* contents2 = CreateWebContents();
2169   tabstrip.AppendWebContents(contents2, true);
2170
2171   // Start watching for events after the appends to avoid observing state
2172   // transitions that aren't relevant to this test.
2173   MockTabStripModelObserver tabstrip_observer(&tabstrip);
2174   tabstrip.AddObserver(&tabstrip_observer);
2175
2176   // Discard one of the tabs.
2177   WebContents* null_contents1 = tabstrip.DiscardWebContentsAt(0);
2178   ASSERT_EQ(2, tabstrip.count());
2179   EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2180   EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2181   ASSERT_EQ(null_contents1, tabstrip.GetWebContentsAt(0));
2182   ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2183   ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2184   State state1(null_contents1, 0, MockTabStripModelObserver::REPLACED);
2185   state1.src_contents = contents1;
2186   EXPECT_TRUE(tabstrip_observer.StateEquals(0, state1));
2187   tabstrip_observer.ClearStates();
2188
2189   // Discard the same tab again.
2190   WebContents* null_contents2 = tabstrip.DiscardWebContentsAt(0);
2191   ASSERT_EQ(2, tabstrip.count());
2192   EXPECT_TRUE(tabstrip.IsTabDiscarded(0));
2193   EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2194   ASSERT_EQ(null_contents2, tabstrip.GetWebContentsAt(0));
2195   ASSERT_EQ(contents2, tabstrip.GetWebContentsAt(1));
2196   ASSERT_EQ(1, tabstrip_observer.GetStateCount());
2197   State state2(null_contents2, 0, MockTabStripModelObserver::REPLACED);
2198   state2.src_contents = null_contents1;
2199   EXPECT_TRUE(tabstrip_observer.StateEquals(0, state2));
2200   tabstrip_observer.ClearStates();
2201
2202   // Activating the tab should clear its discard state.
2203   tabstrip.ActivateTabAt(0, true /* user_gesture */);
2204   ASSERT_EQ(2, tabstrip.count());
2205   EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2206   EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2207
2208   // Don't discard active tab.
2209   tabstrip.DiscardWebContentsAt(0);
2210   ASSERT_EQ(2, tabstrip.count());
2211   EXPECT_FALSE(tabstrip.IsTabDiscarded(0));
2212   EXPECT_FALSE(tabstrip.IsTabDiscarded(1));
2213
2214   tabstrip.CloseAllTabs();
2215 }
2216
2217 // Makes sure TabStripModel handles the case of deleting a tab while removing
2218 // another tab.
2219 TEST_F(TabStripModelTest, DeleteFromDestroy) {
2220   TabStripDummyDelegate delegate;
2221   TabStripModel strip(&delegate, profile());
2222   WebContents* contents1 = CreateWebContents();
2223   WebContents* contents2 = CreateWebContents();
2224   MockTabStripModelObserver tab_strip_model_observer(&strip);
2225   strip.AppendWebContents(contents1, true);
2226   strip.AppendWebContents(contents2, true);
2227   // DeleteWebContentsOnDestroyedObserver deletes contents1 when contents2 sends
2228   // out notification that it is being destroyed.
2229   DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, NULL);
2230   strip.AddObserver(&tab_strip_model_observer);
2231   strip.CloseAllTabs();
2232
2233   int close_all_count = 0, close_all_canceled_count = 0;
2234   tab_strip_model_observer.GetCloseCounts(&close_all_count,
2235                                           &close_all_canceled_count);
2236   EXPECT_EQ(1, close_all_count);
2237   EXPECT_EQ(0, close_all_canceled_count);
2238
2239   strip.RemoveObserver(&tab_strip_model_observer);
2240 }
2241
2242 // Makes sure TabStripModel handles the case of deleting another tab and the
2243 // TabStrip while removing another tab.
2244 TEST_F(TabStripModelTest, DeleteTabStripFromDestroy) {
2245   TabStripDummyDelegate delegate;
2246   TabStripModel* strip = new TabStripModel(&delegate, profile());
2247   MockTabStripModelObserver tab_strip_model_observer(strip);
2248   strip->AddObserver(&tab_strip_model_observer);
2249   WebContents* contents1 = CreateWebContents();
2250   WebContents* contents2 = CreateWebContents();
2251   strip->AppendWebContents(contents1, true);
2252   strip->AppendWebContents(contents2, true);
2253   // DeleteWebContentsOnDestroyedObserver deletes |contents1| and |strip| when
2254   // |contents2| sends out notification that it is being destroyed.
2255   DeleteWebContentsOnDestroyedObserver observer(contents2, contents1, strip);
2256   strip->CloseAllTabs();
2257   EXPECT_TRUE(tab_strip_model_observer.empty());
2258   EXPECT_TRUE(tab_strip_model_observer.deleted());
2259 }
2260
2261 TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
2262   struct TestData {
2263     // Number of tabs the tab strip should have.
2264     const int tab_count;
2265
2266     // Number of pinned tabs.
2267     const int pinned_count;
2268
2269     // Index of the tabs to select.
2270     const std::string selected_tabs;
2271
2272     // Index to move the tabs to.
2273     const int target_index;
2274
2275     // Expected state after the move (space separated list of indices).
2276     const std::string state_after_move;
2277   } test_data[] = {
2278     // 1 selected tab.
2279     { 2, 0, "0", 1, "1 0" },
2280     { 3, 0, "0", 2, "1 2 0" },
2281     { 3, 0, "2", 0, "2 0 1" },
2282     { 3, 0, "2", 1, "0 2 1" },
2283     { 3, 0, "0 1", 0, "0 1 2" },
2284
2285     // 2 selected tabs.
2286     { 6, 0, "4 5", 1, "0 4 5 1 2 3" },
2287     { 3, 0, "0 1", 1, "2 0 1" },
2288     { 4, 0, "0 2", 1, "1 0 2 3" },
2289     { 6, 0, "0 1", 3, "2 3 4 0 1 5" },
2290
2291     // 3 selected tabs.
2292     { 6, 0, "0 2 3", 3, "1 4 5 0 2 3" },
2293     { 7, 0, "4 5 6", 1, "0 4 5 6 1 2 3" },
2294     { 7, 0, "1 5 6", 4, "0 2 3 4 1 5 6" },
2295
2296     // 5 selected tabs.
2297     { 8, 0, "0 2 3 6 7", 3, "1 4 5 0 2 3 6 7" },
2298
2299     // 7 selected tabs
2300     { 16, 0, "0 1 2 3 4 7 9", 8, "5 6 8 10 11 12 13 14 0 1 2 3 4 7 9 15" },
2301
2302     // With pinned tabs.
2303     { 6, 2, "2 3", 2, "0p 1p 2 3 4 5" },
2304     { 6, 2, "0 4", 3, "1p 0p 2 3 4 5" },
2305     { 6, 3, "1 2 4", 0, "1p 2p 0p 4 3 5" },
2306     { 8, 3, "1 3 4", 4, "0p 2p 1p 5 6 3 4 7" },
2307
2308     { 7, 4, "2 3 4", 3, "0p 1p 2p 3p 5 4 6" },
2309   };
2310
2311   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
2312     TabStripDummyDelegate delegate;
2313     TabStripModel strip(&delegate, profile());
2314     ASSERT_NO_FATAL_FAILURE(
2315         PrepareTabstripForSelectionTest(&strip, test_data[i].tab_count,
2316                                         test_data[i].pinned_count,
2317                                         test_data[i].selected_tabs));
2318     strip.MoveSelectedTabsTo(test_data[i].target_index);
2319     EXPECT_EQ(test_data[i].state_after_move,
2320               GetTabStripStateString(strip)) << i;
2321     strip.CloseAllTabs();
2322   }
2323 }
2324
2325 // Tests that moving a tab forgets all groups referencing it.
2326 TEST_F(TabStripModelTest, MoveSelectedTabsTo_ForgetGroups) {
2327   TabStripDummyDelegate delegate;
2328   TabStripModel strip(&delegate, profile());
2329
2330   // Open page A as a new tab and then A1 in the background from A.
2331   WebContents* page_a_contents = CreateWebContents();
2332   strip.AddWebContents(page_a_contents, -1,
2333                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
2334                        TabStripModel::ADD_ACTIVE);
2335   WebContents* page_a1_contents = CreateWebContents();
2336   strip.AddWebContents(page_a1_contents, -1, content::PAGE_TRANSITION_LINK,
2337                        TabStripModel::ADD_NONE);
2338
2339   // Likewise, open pages B and B1.
2340   WebContents* page_b_contents = CreateWebContents();
2341   strip.AddWebContents(page_b_contents, -1,
2342                        content::PAGE_TRANSITION_AUTO_TOPLEVEL,
2343                        TabStripModel::ADD_ACTIVE);
2344   WebContents* page_b1_contents = CreateWebContents();
2345   strip.AddWebContents(page_b1_contents, -1, content::PAGE_TRANSITION_LINK,
2346                        TabStripModel::ADD_NONE);
2347
2348   EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(0));
2349   EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(1));
2350   EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(2));
2351   EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(3));
2352
2353   // Move page B to the start of the tab strip.
2354   strip.MoveSelectedTabsTo(0);
2355
2356   // Open page B2 in the background from B. It should end up after B.
2357   WebContents* page_b2_contents = CreateWebContents();
2358   strip.AddWebContents(page_b2_contents, -1, content::PAGE_TRANSITION_LINK,
2359                        TabStripModel::ADD_NONE);
2360   EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2361   EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2362   EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2363   EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2364   EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(4));
2365
2366   // Switch to A.
2367   strip.ActivateTabAt(2, true);
2368   EXPECT_EQ(page_a_contents, strip.GetActiveWebContents());
2369
2370   // Open page A2 in the background from A. It should end up after A1.
2371   WebContents* page_a2_contents = CreateWebContents();
2372   strip.AddWebContents(page_a2_contents, -1, content::PAGE_TRANSITION_LINK,
2373                        TabStripModel::ADD_NONE);
2374   EXPECT_EQ(page_b_contents, strip.GetWebContentsAt(0));
2375   EXPECT_EQ(page_b2_contents, strip.GetWebContentsAt(1));
2376   EXPECT_EQ(page_a_contents, strip.GetWebContentsAt(2));
2377   EXPECT_EQ(page_a1_contents, strip.GetWebContentsAt(3));
2378   EXPECT_EQ(page_a2_contents, strip.GetWebContentsAt(4));
2379   EXPECT_EQ(page_b1_contents, strip.GetWebContentsAt(5));
2380
2381   strip.CloseAllTabs();
2382 }
2383
2384 TEST_F(TabStripModelTest, CloseSelectedTabs) {
2385   TabStripDummyDelegate delegate;
2386   TabStripModel strip(&delegate, profile());
2387   WebContents* contents1 = CreateWebContents();
2388   WebContents* contents2 = CreateWebContents();
2389   WebContents* contents3 = CreateWebContents();
2390   strip.AppendWebContents(contents1, true);
2391   strip.AppendWebContents(contents2, true);
2392   strip.AppendWebContents(contents3, true);
2393   strip.ToggleSelectionAt(1);
2394   strip.CloseSelectedTabs();
2395   EXPECT_EQ(1, strip.count());
2396   EXPECT_EQ(0, strip.active_index());
2397   strip.CloseAllTabs();
2398 }
2399
2400 TEST_F(TabStripModelTest, MultipleSelection) {
2401   typedef MockTabStripModelObserver::State State;
2402
2403   TabStripDummyDelegate delegate;
2404   TabStripModel strip(&delegate, profile());
2405   MockTabStripModelObserver observer(&strip);
2406   WebContents* contents0 = CreateWebContents();
2407   WebContents* contents1 = CreateWebContents();
2408   WebContents* contents2 = CreateWebContents();
2409   WebContents* contents3 = CreateWebContents();
2410   strip.AppendWebContents(contents0, false);
2411   strip.AppendWebContents(contents1, false);
2412   strip.AppendWebContents(contents2, false);
2413   strip.AppendWebContents(contents3, false);
2414   strip.AddObserver(&observer);
2415
2416   // Selection and active tab change.
2417   strip.ActivateTabAt(3, true);
2418   ASSERT_EQ(2, observer.GetStateCount());
2419   ASSERT_EQ(observer.GetStateAt(0).action,
2420             MockTabStripModelObserver::ACTIVATE);
2421   State s1(contents3, 3, MockTabStripModelObserver::SELECT);
2422   EXPECT_TRUE(observer.StateEquals(1, s1));
2423   observer.ClearStates();
2424
2425   // Adding all tabs to selection, active tab is now at 0.
2426   strip.ExtendSelectionTo(0);
2427   ASSERT_EQ(3, observer.GetStateCount());
2428   ASSERT_EQ(observer.GetStateAt(0).action,
2429             MockTabStripModelObserver::DEACTIVATE);
2430   ASSERT_EQ(observer.GetStateAt(1).action,
2431             MockTabStripModelObserver::ACTIVATE);
2432   State s2(contents0, 0, MockTabStripModelObserver::SELECT);
2433   s2.src_contents = contents3;
2434   s2.src_index = 3;
2435   EXPECT_TRUE(observer.StateEquals(2, s2));
2436   observer.ClearStates();
2437
2438   // Toggle the active tab, should make the next index active.
2439   strip.ToggleSelectionAt(0);
2440   EXPECT_EQ(1, strip.active_index());
2441   EXPECT_EQ(3U, strip.selection_model().size());
2442   EXPECT_EQ(4, strip.count());
2443   ASSERT_EQ(3, observer.GetStateCount());
2444   ASSERT_EQ(observer.GetStateAt(0).action,
2445             MockTabStripModelObserver::DEACTIVATE);
2446   ASSERT_EQ(observer.GetStateAt(1).action,
2447             MockTabStripModelObserver::ACTIVATE);
2448   ASSERT_EQ(observer.GetStateAt(2).action,
2449             MockTabStripModelObserver::SELECT);
2450   observer.ClearStates();
2451
2452   // Toggle the first tab back to selected and active.
2453   strip.ToggleSelectionAt(0);
2454   EXPECT_EQ(0, strip.active_index());
2455   EXPECT_EQ(4U, strip.selection_model().size());
2456   EXPECT_EQ(4, strip.count());
2457   ASSERT_EQ(3, observer.GetStateCount());
2458   ASSERT_EQ(observer.GetStateAt(0).action,
2459             MockTabStripModelObserver::DEACTIVATE);
2460   ASSERT_EQ(observer.GetStateAt(1).action,
2461             MockTabStripModelObserver::ACTIVATE);
2462   ASSERT_EQ(observer.GetStateAt(2).action,
2463             MockTabStripModelObserver::SELECT);
2464   observer.ClearStates();
2465
2466   // Closing one of the selected tabs, not the active one.
2467   strip.CloseWebContentsAt(1, TabStripModel::CLOSE_NONE);
2468   EXPECT_EQ(3, strip.count());
2469   ASSERT_EQ(3, observer.GetStateCount());
2470   ASSERT_EQ(observer.GetStateAt(0).action,
2471             MockTabStripModelObserver::CLOSE);
2472   ASSERT_EQ(observer.GetStateAt(1).action,
2473             MockTabStripModelObserver::DETACH);
2474   ASSERT_EQ(observer.GetStateAt(2).action,
2475             MockTabStripModelObserver::SELECT);
2476   observer.ClearStates();
2477
2478   // Closing the active tab, while there are others tabs selected.
2479   strip.CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
2480   EXPECT_EQ(2, strip.count());
2481   ASSERT_EQ(5, observer.GetStateCount());
2482   ASSERT_EQ(observer.GetStateAt(0).action,
2483             MockTabStripModelObserver::CLOSE);
2484   ASSERT_EQ(observer.GetStateAt(1).action,
2485             MockTabStripModelObserver::DETACH);
2486   ASSERT_EQ(observer.GetStateAt(2).action,
2487             MockTabStripModelObserver::DEACTIVATE);
2488   ASSERT_EQ(observer.GetStateAt(3).action,
2489             MockTabStripModelObserver::ACTIVATE);
2490   ASSERT_EQ(observer.GetStateAt(4).action,
2491             MockTabStripModelObserver::SELECT);
2492   observer.ClearStates();
2493
2494   // Active tab is at 0, deselecting all but the active tab.
2495   strip.ToggleSelectionAt(1);
2496   ASSERT_EQ(1, observer.GetStateCount());
2497   ASSERT_EQ(observer.GetStateAt(0).action,
2498             MockTabStripModelObserver::SELECT);
2499   observer.ClearStates();
2500
2501   // Attempting to deselect the only selected and therefore active tab,
2502   // it is ignored (no notifications being sent) and tab at 0 remains selected
2503   // and active.
2504   strip.ToggleSelectionAt(0);
2505   ASSERT_EQ(0, observer.GetStateCount());
2506
2507   strip.RemoveObserver(&observer);
2508   strip.CloseAllTabs();
2509 }
2510
2511 // Verifies that if we change the selection from a multi selection to a single
2512 // selection, but not in a way that changes the selected_index that
2513 // TabSelectionChanged is invoked.
2514 TEST_F(TabStripModelTest, MultipleToSingle) {
2515   typedef MockTabStripModelObserver::State State;
2516
2517   TabStripDummyDelegate delegate;
2518   TabStripModel strip(&delegate, profile());
2519   WebContents* contents1 = CreateWebContents();
2520   WebContents* contents2 = CreateWebContents();
2521   strip.AppendWebContents(contents1, false);
2522   strip.AppendWebContents(contents2, false);
2523   strip.ToggleSelectionAt(0);
2524   strip.ToggleSelectionAt(1);
2525
2526   MockTabStripModelObserver observer(&strip);
2527   strip.AddObserver(&observer);
2528   // This changes the selection (0 is no longer selected) but the selected_index
2529   // still remains at 1.
2530   strip.ActivateTabAt(1, true);
2531   ASSERT_EQ(1, observer.GetStateCount());
2532   State s(contents2, 1, MockTabStripModelObserver::SELECT);
2533   s.src_contents = contents2;
2534   s.src_index = 1;
2535   s.change_reason = TabStripModelObserver::CHANGE_REASON_NONE;
2536   EXPECT_TRUE(observer.StateEquals(0, s));
2537   strip.RemoveObserver(&observer);
2538   strip.CloseAllTabs();
2539 }
2540
2541 // Verifies a newly inserted tab retains its previous blocked state.
2542 // http://crbug.com/276334
2543 TEST_F(TabStripModelTest, TabBlockedState) {
2544   // Start with a source tab strip.
2545   TabStripDummyDelegate dummy_tab_strip_delegate;
2546   TabStripModel strip_src(&dummy_tab_strip_delegate, profile());
2547   TabBlockedStateTestBrowser browser_src(&strip_src);
2548
2549   // Add a tab.
2550   WebContents* contents1 = CreateWebContents();
2551   web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
2552   strip_src.AppendWebContents(contents1, false);
2553
2554   // Add another tab.
2555   WebContents* contents2 = CreateWebContents();
2556   web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2);
2557   strip_src.AppendWebContents(contents2, false);
2558
2559   // Create a destination tab strip.
2560   TabStripModel strip_dst(&dummy_tab_strip_delegate, profile());
2561   TabBlockedStateTestBrowser browser_dst(&strip_dst);
2562
2563   // Setup a SingleWebContentsDialogManager for tab |contents2|.
2564   web_modal::WebContentsModalDialogManager* modal_dialog_manager =
2565       web_modal::WebContentsModalDialogManager::FromWebContents(contents2);
2566   web_modal::WebContentsModalDialogManager::TestApi test_api(
2567       modal_dialog_manager);
2568
2569   // Show a dialog that blocks tab |contents2|.
2570   // DummySingleWebContentsDialogManager doesn't care about the
2571   // NativeWebContentsModalDialog value, so any dummy value works.
2572   DummySingleWebContentsDialogManager* native_manager =
2573       new DummySingleWebContentsDialogManager(
2574           reinterpret_cast<NativeWebContentsModalDialog>(0),
2575           modal_dialog_manager);
2576   modal_dialog_manager->ShowDialogWithManager(
2577       reinterpret_cast<NativeWebContentsModalDialog>(0),
2578       scoped_ptr<web_modal::SingleWebContentsDialogManager>(
2579           native_manager).Pass());
2580   EXPECT_TRUE(strip_src.IsTabBlocked(1));
2581
2582   // Detach the tab.
2583   WebContents* moved_contents = strip_src.DetachWebContentsAt(1);
2584   EXPECT_EQ(contents2, moved_contents);
2585
2586   // Attach the tab to the destination tab strip.
2587   strip_dst.AppendWebContents(moved_contents, true);
2588   EXPECT_TRUE(strip_dst.IsTabBlocked(0));
2589
2590   strip_dst.CloseAllTabs();
2591   strip_src.CloseAllTabs();
2592 }