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