OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ui/tabs/mru_tab_controller.h" |
| 6 |
| 7 #include <map> |
| 8 #include <string> |
| 9 |
| 10 #include "base/stl_util.h" |
| 11 #include "base/string_number_conversions.h" |
| 12 #include "base/string_split.h" |
| 13 #include "base/utf_string_conversions.h" |
| 14 #include "chrome/browser/ui/browser.h" |
| 15 #include "chrome/browser/ui/browser_tabstrip.h" |
| 16 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
| 17 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 18 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" |
| 19 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h" |
| 20 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" |
| 21 #include "chrome/common/url_constants.h" |
| 22 #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| 23 #include "chrome/test/base/testing_profile.h" |
| 24 #include "content/public/browser/web_contents.h" |
| 25 #include "content/public/test/test_browser_thread.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" |
| 27 |
| 28 using content::BrowserThread; |
| 29 using content::SiteInstance; |
| 30 using content::WebContents; |
| 31 |
| 32 class TabStripDummyDelegate : public TestTabStripModelDelegate { |
| 33 public: |
| 34 explicit TabStripDummyDelegate(TabContents* dummy) |
| 35 : dummy_contents_(dummy), |
| 36 can_close_(true), |
| 37 run_unload_(false) {} |
| 38 virtual ~TabStripDummyDelegate() {} |
| 39 |
| 40 void set_can_close(bool value) { can_close_ = value; } |
| 41 void set_run_unload_listener(bool value) { run_unload_ = value; } |
| 42 |
| 43 // Overridden from TabStripModelDelegate: |
| 44 virtual TabContents* CreateTabContentsForURL( |
| 45 const GURL& url, |
| 46 const content::Referrer& referrer, |
| 47 Profile* profile, |
| 48 content::PageTransition transition, |
| 49 bool defer_load, |
| 50 SiteInstance* instance) const OVERRIDE { |
| 51 if (url == GURL(chrome::kChromeUINewTabURL)) |
| 52 return dummy_contents_; |
| 53 return NULL; |
| 54 } |
| 55 virtual bool RunUnloadListenerBeforeClosing(TabContents* contents) OVERRIDE { |
| 56 return run_unload_; |
| 57 } |
| 58 virtual bool CanCloseContents(std::vector<int>* indices) OVERRIDE { |
| 59 if (!can_close_) |
| 60 indices->clear(); |
| 61 return can_close_; |
| 62 } |
| 63 |
| 64 private: |
| 65 // A dummy TabContents we give to callers that expect us to actually |
| 66 // build a Destinations tab for them. |
| 67 TabContents* dummy_contents_; |
| 68 |
| 69 // Whether tabs can be closed. |
| 70 bool can_close_; |
| 71 |
| 72 // Whether to report that we need to run an unload listener before closing. |
| 73 bool run_unload_; |
| 74 |
| 75 DISALLOW_COPY_AND_ASSIGN(TabStripDummyDelegate); |
| 76 }; |
| 77 |
| 78 class MRUTabControllerTest : public ChromeRenderViewHostTestHarness { |
| 79 public: |
| 80 MRUTabControllerTest() : browser_thread_(BrowserThread::UI, &message_loop_) { |
| 81 } |
| 82 |
| 83 TabContents* CreateTabContents() { |
| 84 return chrome::TabContentsFactory( |
| 85 profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); |
| 86 } |
| 87 |
| 88 TabContents* CreateTabContentsWithSharedRPH( |
| 89 WebContents* web_contents) { |
| 90 TabContents* retval = chrome::TabContentsFactory(profile(), |
| 91 web_contents->GetRenderViewHost()->GetSiteInstance(), MSG_ROUTING_NONE, |
| 92 NULL, NULL); |
| 93 EXPECT_EQ(retval->web_contents()->GetRenderProcessHost(), |
| 94 web_contents->GetRenderProcessHost()); |
| 95 return retval; |
| 96 } |
| 97 |
| 98 // Forwards a URL "load" request through to our dummy TabContents |
| 99 // implementation. |
| 100 void LoadURL(WebContents* con, const std::wstring& url) { |
| 101 controller().LoadURL(GURL(WideToUTF16(url)), content::Referrer(), |
| 102 content::PAGE_TRANSITION_LINK, std::string()); |
| 103 } |
| 104 |
| 105 void GoBack(WebContents* contents) { |
| 106 controller().GoBack(); |
| 107 } |
| 108 |
| 109 void GoForward(WebContents* contents) { |
| 110 controller().GoForward(); |
| 111 } |
| 112 |
| 113 void SwitchTabTo(WebContents* contents) { |
| 114 // contents()->DidBecomeSelected(); |
| 115 } |
| 116 |
| 117 // Sets the id of the specified contents. |
| 118 void SetID(WebContents* contents, int id) { |
| 119 GetIDAccessor()->SetProperty(contents->GetPropertyBag(), id); |
| 120 } |
| 121 |
| 122 // Returns the id of the specified contents. |
| 123 int GetID(WebContents* contents) { |
| 124 return *GetIDAccessor()->GetProperty(contents->GetPropertyBag()); |
| 125 } |
| 126 |
| 127 // Returns the state of the given tab strip as a string. The state consists |
| 128 // of the ID of each tab contents followed by a 'p' if pinned. For example, |
| 129 // if the model consists of two tabs with ids 2 and 1, with the first |
| 130 // tab pinned, this returns "2p 1". |
| 131 std::string GetPinnedState(const TabStripModel& model) { |
| 132 std::string actual; |
| 133 for (int i = 0; i < model.count(); ++i) { |
| 134 if (i > 0) |
| 135 actual += " "; |
| 136 |
| 137 actual += |
| 138 base::IntToString(GetID(model.GetTabContentsAt(i)->web_contents())); |
| 139 |
| 140 if (model.IsAppTab(i)) |
| 141 actual += "a"; |
| 142 |
| 143 if (model.IsTabPinned(i)) |
| 144 actual += "p"; |
| 145 } |
| 146 return actual; |
| 147 } |
| 148 |
| 149 std::string GetIndicesClosedByCommandAsString( |
| 150 const TabStripModel& model, |
| 151 int index, |
| 152 TabStripModel::ContextMenuCommand id) const { |
| 153 std::vector<int> indices = model.GetIndicesClosedByCommand(index, id); |
| 154 std::string result; |
| 155 for (size_t i = 0; i < indices.size(); ++i) { |
| 156 if (i != 0) |
| 157 result += " "; |
| 158 result += base::IntToString(indices[i]); |
| 159 } |
| 160 return result; |
| 161 } |
| 162 |
| 163 void PrepareTabstripForSelectionTest(TabStripModel* model, |
| 164 int tab_count, |
| 165 int pinned_count, |
| 166 const std::string& selected_tabs) { |
| 167 for (int i = 0; i < tab_count; ++i) { |
| 168 TabContents* contents = CreateTabContents(); |
| 169 SetID(contents->web_contents(), i); |
| 170 model->AppendTabContents(contents, true); |
| 171 } |
| 172 for (int i = 0; i < pinned_count; ++i) |
| 173 model->SetTabPinned(i, true); |
| 174 |
| 175 TabStripSelectionModel selection_model; |
| 176 std::vector<std::string> selection; |
| 177 base::SplitStringAlongWhitespace(selected_tabs, &selection); |
| 178 for (size_t i = 0; i < selection.size(); ++i) { |
| 179 int value; |
| 180 ASSERT_TRUE(base::StringToInt(selection[i], &value)); |
| 181 selection_model.AddIndexToSelection(value); |
| 182 } |
| 183 selection_model.set_active(selection_model.selected_indices()[0]); |
| 184 model->SetSelectionFromModel(selection_model); |
| 185 } |
| 186 |
| 187 private: |
| 188 base::PropertyAccessor<int>* GetIDAccessor() { |
| 189 static base::PropertyAccessor<int> accessor; |
| 190 return &accessor; |
| 191 } |
| 192 |
| 193 content::TestBrowserThread browser_thread_; |
| 194 |
| 195 std::wstring test_dir_; |
| 196 std::wstring profile_path_; |
| 197 }; |
| 198 |
| 199 static int GetInsertionIndex(TabStripModel* tabstrip, |
| 200 TabContents* contents) { |
| 201 return tabstrip->order_controller()->DetermineInsertionIndex( |
| 202 contents, content::PAGE_TRANSITION_LINK, false); |
| 203 } |
| 204 |
| 205 static void InsertTabContentses(TabStripModel* tabstrip, |
| 206 TabContents* contents1, |
| 207 TabContents* contents2, |
| 208 TabContents* contents3) { |
| 209 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents1), |
| 210 contents1, TabStripModel::ADD_INHERIT_GROUP); |
| 211 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents2), |
| 212 contents2, TabStripModel::ADD_INHERIT_GROUP); |
| 213 tabstrip->InsertTabContentsAt(GetInsertionIndex(tabstrip, contents3), |
| 214 contents3, TabStripModel::ADD_INHERIT_GROUP); |
| 215 } |
| 216 |
| 217 // Test toggling between mru tabs |
| 218 TEST_F(MRUTabControllerTest, ToggleMRUTabs) { |
| 219 TabStripDummyDelegate delegate(NULL); |
| 220 TabStripModel tabstrip(&delegate, profile()); |
| 221 MRUTabController controller(&tabstrip); |
| 222 |
| 223 EXPECT_TRUE(tabstrip.empty()); |
| 224 |
| 225 TabContents* opener_contents = CreateTabContents(); |
| 226 tabstrip.AppendTabContents(opener_contents, true); |
| 227 |
| 228 TabContents* contents1 = CreateTabContents(); |
| 229 TabContents* contents2 = CreateTabContents(); |
| 230 TabContents* contents3 = CreateTabContents(); |
| 231 |
| 232 InsertTabContentses(&tabstrip, contents1, contents2, contents3); |
| 233 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1)); |
| 234 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2)); |
| 235 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3)); |
| 236 |
| 237 EXPECT_EQ(0, tabstrip.active_index()); |
| 238 tabstrip.ActivateTabAt(2, true); |
| 239 EXPECT_EQ(2, tabstrip.active_index()); |
| 240 |
| 241 EXPECT_EQ(0, tabstrip.GetIndexOfTabContents(controller.GetPreviousMRUTab())); |
| 242 |
| 243 tabstrip.CloseAllTabs(); |
| 244 // TabStripModel should now be empty. |
| 245 EXPECT_TRUE(tabstrip.empty()); |
| 246 } |
| 247 |
| 248 // Test toggling between mru tabs |
| 249 TEST_F(MRUTabControllerTest, PauseStackAndToggle) { |
| 250 TabStripDummyDelegate delegate(NULL); |
| 251 TabStripModel tabstrip(&delegate, profile()); |
| 252 MRUTabController controller(&tabstrip); |
| 253 |
| 254 EXPECT_TRUE(tabstrip.empty()); |
| 255 |
| 256 TabContents* opener_contents = CreateTabContents(); |
| 257 tabstrip.AppendTabContents(opener_contents, true); |
| 258 |
| 259 TabContents* contents1 = CreateTabContents(); |
| 260 TabContents* contents2 = CreateTabContents(); |
| 261 TabContents* contents3 = CreateTabContents(); |
| 262 |
| 263 InsertTabContentses(&tabstrip, contents1, contents2, contents3); |
| 264 EXPECT_EQ(contents1, tabstrip.GetTabContentsAt(1)); |
| 265 EXPECT_EQ(contents2, tabstrip.GetTabContentsAt(2)); |
| 266 EXPECT_EQ(contents3, tabstrip.GetTabContentsAt(3)); |
| 267 |
| 268 EXPECT_EQ(0, tabstrip.active_index()); |
| 269 controller.PauseStackUpdates(); |
| 270 tabstrip.ActivateTabAt(1, true); |
| 271 EXPECT_EQ(1, tabstrip.active_index()); |
| 272 tabstrip.ActivateTabAt(2, true); |
| 273 EXPECT_EQ(2, tabstrip.active_index()); |
| 274 tabstrip.ActivateTabAt(3, true); |
| 275 EXPECT_EQ(3, tabstrip.active_index()); |
| 276 |
| 277 // Commit the active tab changes now. |
| 278 controller.CommitActiveTabChanges(); |
| 279 tabstrip.ActivateTabAt(0, true); |
| 280 EXPECT_EQ(0, tabstrip.active_index()); |
| 281 |
| 282 // Previous MRU tab would be 3 |
| 283 EXPECT_EQ(3, tabstrip.GetIndexOfTabContents(controller.GetPreviousMRUTab())); |
| 284 |
| 285 tabstrip.CloseAllTabs(); |
| 286 // TabStripModel should now be empty. |
| 287 EXPECT_TRUE(tabstrip.empty()); |
| 288 } |
OLD | NEW |