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