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