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/views/ash/tab_scrubber.h" |
| 6 |
| 7 #include "ash/display/event_transformation_handler.h" |
| 8 #include "ash/shell.h" |
| 9 #include "base/command_line.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/message_loop.h" |
| 12 #include "base/message_loop_proxy.h" |
| 13 #include "chrome/browser/ui/browser_tabstrip.h" |
| 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 15 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" |
| 16 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 17 #include "chrome/browser/ui/views/tabs/tab.h" |
| 18 #include "chrome/browser/ui/views/tabs/tab_strip.h" |
| 19 #include "chrome/common/chrome_switches.h" |
| 20 #include "chrome/test/base/in_process_browser_test.h" |
| 21 #include "chrome/test/base/ui_test_utils.h" |
| 22 #include "content/public/browser/browser_thread.h" |
| 23 #include "content/public/browser/notification_service.h" |
| 24 #include "content/public/common/url_constants.h" |
| 25 #include "content/public/test/test_utils.h" |
| 26 #include "ui/aura/test/event_generator.h" |
| 27 #include "ui/aura/window.h" |
| 28 #include "ui/base/events/event_utils.h" |
| 29 |
| 30 namespace { |
| 31 |
| 32 class TabScrubberTest : public InProcessBrowserTest, |
| 33 public TabStripModelObserver { |
| 34 public: |
| 35 TabScrubberTest() |
| 36 : target_index_(-1) { |
| 37 } |
| 38 |
| 39 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| 40 #if defined(OS_CHROMEOS) |
| 41 command_line->AppendSwitch(switches::kNaturalScrollDefault); |
| 42 #endif |
| 43 command_line->AppendSwitch(switches::kAshEnableTabScrubbing); |
| 44 command_line->AppendSwitch(switches::kOpenAsh); |
| 45 } |
| 46 |
| 47 virtual void SetUpOnMainThread() OVERRIDE { |
| 48 TabScrubber::GetInstance()->set_activation_delay( |
| 49 base::TimeDelta::FromMilliseconds(0)); |
| 50 |
| 51 // Disable external monitor scaling of coordinates. |
| 52 ash::Shell* shell = ash::Shell::GetInstance(); |
| 53 shell->event_transformation_handler()->set_transformation_mode( |
| 54 ash::internal::EventTransformationHandler::TRANSFORM_NONE); |
| 55 } |
| 56 |
| 57 virtual void CleanUpOnMainThread() { |
| 58 browser()->tab_strip_model()->RemoveObserver(this); |
| 59 } |
| 60 |
| 61 TabStrip* GetTabStrip(Browser* browser) { |
| 62 aura::Window* window = browser->window()->GetNativeWindow(); |
| 63 return BrowserView::GetBrowserViewForNativeWindow(window)->tabstrip(); |
| 64 } |
| 65 |
| 66 int GetStartX(Browser* browser, |
| 67 int index, |
| 68 TabScrubber::Direction direction) { |
| 69 return TabScrubber::GetStartPoint( |
| 70 GetTabStrip(browser), index, direction).x(); |
| 71 } |
| 72 |
| 73 int GetTabCenter(Browser* browser, int index) { |
| 74 return GetTabStrip(browser)->tab_at(index)->bounds().CenterPoint().x(); |
| 75 } |
| 76 |
| 77 // Sends one scroll event synchronously without initial or final |
| 78 // fling events. |
| 79 void SendScrubEvent(Browser* browser, int index) { |
| 80 aura::Window* window = browser->window()->GetNativeWindow(); |
| 81 aura::RootWindow* root = window->GetRootWindow(); |
| 82 aura::test::EventGenerator event_generator(root, window); |
| 83 int active_index = browser->tab_strip_model()->active_index(); |
| 84 TabScrubber::Direction direction = index < active_index ? |
| 85 TabScrubber::LEFT : TabScrubber::RIGHT; |
| 86 int offset = GetTabCenter(browser, index) - |
| 87 GetStartX(browser, active_index, direction); |
| 88 ui::ScrollEvent scroll_event(ui::ET_SCROLL, |
| 89 gfx::Point(0, 0), |
| 90 ui::EventTimeForNow(), |
| 91 0, |
| 92 offset, |
| 93 0, |
| 94 3); |
| 95 event_generator.Dispatch(&scroll_event); |
| 96 } |
| 97 |
| 98 enum ScrubType { |
| 99 EACH_TAB, |
| 100 SKIP_TABS, |
| 101 REPEAT_TABS, |
| 102 }; |
| 103 |
| 104 // Sends asynchronous events and waits for tab at |index| to become |
| 105 // active. |
| 106 void Scrub(Browser* browser, int index, ScrubType scrub_type) { |
| 107 aura::Window* window = browser->window()->GetNativeWindow(); |
| 108 aura::RootWindow* root = window->GetRootWindow(); |
| 109 aura::test::EventGenerator event_generator(root, window); |
| 110 event_generator.set_async(true); |
| 111 activation_order_.clear(); |
| 112 int active_index = browser->tab_strip_model()->active_index(); |
| 113 ASSERT_NE(index, active_index); |
| 114 ASSERT_TRUE(scrub_type != SKIP_TABS || ((index - active_index) % 2) == 0); |
| 115 TabScrubber::Direction direction; |
| 116 int increment; |
| 117 if (index < active_index) { |
| 118 direction = TabScrubber::LEFT; |
| 119 increment = -1; |
| 120 } else { |
| 121 direction = TabScrubber::RIGHT; |
| 122 increment = 1; |
| 123 } |
| 124 if (scrub_type == SKIP_TABS) |
| 125 increment *= 2; |
| 126 int last = GetStartX(browser, active_index, direction); |
| 127 std::vector<gfx::Point> offsets; |
| 128 for (int i = active_index + increment; i != (index + increment); |
| 129 i += increment) { |
| 130 int tab_center = GetTabCenter(browser, i); |
| 131 offsets.push_back(gfx::Point(tab_center - last, 0)); |
| 132 last = GetStartX(browser, i, direction); |
| 133 if (scrub_type == REPEAT_TABS) { |
| 134 offsets.push_back(gfx::Point(increment, 0)); |
| 135 last += increment; |
| 136 } |
| 137 } |
| 138 event_generator.ScrollSequence(gfx::Point(0, 0), |
| 139 base::TimeDelta::FromMilliseconds(100), |
| 140 offsets, |
| 141 3); |
| 142 RunUntilTabActive(browser, index); |
| 143 } |
| 144 |
| 145 // Sends events and waits for tab at |index| to become active |
| 146 // if it's different from the currently active tab. |
| 147 // If the active tab is expected to stay the same, send events |
| 148 // synchronously (as we don't have anything to wait for). |
| 149 void SendScrubSequence(Browser* browser, int x_offset, int index) { |
| 150 aura::Window* window = browser->window()->GetNativeWindow(); |
| 151 aura::RootWindow* root = window->GetRootWindow(); |
| 152 aura::test::EventGenerator event_generator(root, window); |
| 153 bool wait_for_active = false; |
| 154 if (index != browser->tab_strip_model()->active_index()) { |
| 155 wait_for_active = true; |
| 156 event_generator.set_async(true); |
| 157 } |
| 158 event_generator.ScrollSequence(gfx::Point(0, 0), |
| 159 ui::EventTimeForNow(), |
| 160 x_offset, |
| 161 0, |
| 162 1, |
| 163 3); |
| 164 if (wait_for_active) |
| 165 RunUntilTabActive(browser, index); |
| 166 } |
| 167 |
| 168 void AddTabs(Browser* browser, int num_tabs) { |
| 169 TabStrip* tab_strip = GetTabStrip(browser); |
| 170 for (int i = 0; i < num_tabs; ++i) |
| 171 AddBlankTabAndShow(browser); |
| 172 ASSERT_EQ(num_tabs + 1, browser->tab_strip_model()->count()); |
| 173 ASSERT_EQ(num_tabs, browser->tab_strip_model()->active_index()); |
| 174 tab_strip->StopAnimating(true); |
| 175 ASSERT_FALSE(tab_strip->IsAnimating()); |
| 176 } |
| 177 |
| 178 // TabStripModelObserver overrides. |
| 179 virtual void TabInsertedAt(content::WebContents* contents, |
| 180 int index, |
| 181 bool foreground) {} |
| 182 virtual void TabClosingAt(TabStripModel* tab_strip_model, |
| 183 content::WebContents* contents, |
| 184 int index) {} |
| 185 virtual void TabDetachedAt(content::WebContents* contents, int index) {} |
| 186 virtual void TabDeactivated(content::WebContents* contents) {} |
| 187 virtual void ActiveTabChanged(content::WebContents* old_contents, |
| 188 content::WebContents* new_contents, |
| 189 int index, |
| 190 bool user_gesture) { |
| 191 activation_order_.push_back(index); |
| 192 if (index == target_index_) |
| 193 quit_closure_.Run(); |
| 194 } |
| 195 |
| 196 virtual void TabSelectionChanged(TabStripModel* tab_strip_model, |
| 197 const ui::ListSelectionModel& old_model) {} |
| 198 virtual void TabMoved(content::WebContents* contents, |
| 199 int from_index, |
| 200 int to_index) {} |
| 201 virtual void TabChangedAt(content::WebContents* contents, |
| 202 int index, |
| 203 TabChangeType change_type) {} |
| 204 virtual void TabReplacedAt(TabStripModel* tab_strip_model, |
| 205 content::WebContents* old_contents, |
| 206 content::WebContents* new_contents, |
| 207 int index) {} |
| 208 virtual void TabPinnedStateChanged(content::WebContents* contents, |
| 209 int index) {} |
| 210 virtual void TabMiniStateChanged(content::WebContents* contents, int index) {} |
| 211 virtual void TabBlockedStateChanged(content::WebContents* contents, |
| 212 int index) {} |
| 213 virtual void TabStripEmpty() {} |
| 214 virtual void TabStripModelDeleted() {} |
| 215 |
| 216 // History of tab activation. Scrub() resets it. |
| 217 std::vector<int> activation_order_; |
| 218 |
| 219 private: |
| 220 void RunUntilTabActive(Browser* browser, int target) { |
| 221 base::RunLoop run_loop; |
| 222 quit_closure_ = content::GetQuitTaskForRunLoop(&run_loop); |
| 223 browser->tab_strip_model()->AddObserver(this); |
| 224 target_index_ = target; |
| 225 content::RunThisRunLoop(&run_loop); |
| 226 browser->tab_strip_model()->RemoveObserver(this); |
| 227 target_index_ = -1; |
| 228 } |
| 229 |
| 230 base::Closure quit_closure_; |
| 231 int target_index_; |
| 232 |
| 233 DISALLOW_COPY_AND_ASSIGN(TabScrubberTest); |
| 234 }; |
| 235 |
| 236 } // namespace |
| 237 |
| 238 #if defined(OS_CHROMEOS) |
| 239 // Swipe a single tab in each direction. |
| 240 IN_PROC_BROWSER_TEST_F(TabScrubberTest, Single) { |
| 241 AddTabs(browser(), 1); |
| 242 |
| 243 Scrub(browser(), 0, EACH_TAB); |
| 244 EXPECT_EQ(1U, activation_order_.size()); |
| 245 EXPECT_EQ(0, activation_order_[0]); |
| 246 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 247 |
| 248 Scrub(browser(), 1, EACH_TAB); |
| 249 EXPECT_EQ(1U, activation_order_.size()); |
| 250 EXPECT_EQ(1, activation_order_[0]); |
| 251 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); |
| 252 } |
| 253 |
| 254 // Swipe 4 tabs in each direction. Each of the tabs should become active. |
| 255 IN_PROC_BROWSER_TEST_F(TabScrubberTest, Multi) { |
| 256 AddTabs(browser(), 4); |
| 257 |
| 258 Scrub(browser(), 0, EACH_TAB); |
| 259 ASSERT_EQ(4U, activation_order_.size()); |
| 260 EXPECT_EQ(3, activation_order_[0]); |
| 261 EXPECT_EQ(2, activation_order_[1]); |
| 262 EXPECT_EQ(1, activation_order_[2]); |
| 263 EXPECT_EQ(0, activation_order_[3]); |
| 264 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 265 |
| 266 Scrub(browser(), 4, EACH_TAB); |
| 267 ASSERT_EQ(4U, activation_order_.size()); |
| 268 EXPECT_EQ(1, activation_order_[0]); |
| 269 EXPECT_EQ(2, activation_order_[1]); |
| 270 EXPECT_EQ(3, activation_order_[2]); |
| 271 EXPECT_EQ(4, activation_order_[3]); |
| 272 EXPECT_EQ(4, browser()->tab_strip_model()->active_index()); |
| 273 } |
| 274 |
| 275 IN_PROC_BROWSER_TEST_F(TabScrubberTest, MultiBrowser) { |
| 276 AddTabs(browser(), 1); |
| 277 Scrub(browser(), 0, EACH_TAB); |
| 278 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 279 |
| 280 Browser* browser2 = CreateBrowser(browser()->profile()); |
| 281 browser2->window()->Activate(); |
| 282 ASSERT_TRUE(browser2->window()->IsActive()); |
| 283 ASSERT_FALSE(browser()->window()->IsActive()); |
| 284 AddTabs(browser2, 1); |
| 285 |
| 286 Scrub(browser2, 0, EACH_TAB); |
| 287 EXPECT_EQ(0, browser2->tab_strip_model()->active_index()); |
| 288 } |
| 289 |
| 290 // Swipe 4 tabs in each direction with an extra swipe within each. The same |
| 291 // 4 tabs should become active. |
| 292 IN_PROC_BROWSER_TEST_F(TabScrubberTest, Repeated) { |
| 293 AddTabs(browser(), 4); |
| 294 |
| 295 Scrub(browser(), 0, REPEAT_TABS); |
| 296 ASSERT_EQ(4U, activation_order_.size()); |
| 297 EXPECT_EQ(3, activation_order_[0]); |
| 298 EXPECT_EQ(2, activation_order_[1]); |
| 299 EXPECT_EQ(1, activation_order_[2]); |
| 300 EXPECT_EQ(0, activation_order_[3]); |
| 301 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 302 |
| 303 Scrub(browser(), 4, REPEAT_TABS); |
| 304 ASSERT_EQ(4U, activation_order_.size()); |
| 305 EXPECT_EQ(1, activation_order_[0]); |
| 306 EXPECT_EQ(2, activation_order_[1]); |
| 307 EXPECT_EQ(3, activation_order_[2]); |
| 308 EXPECT_EQ(4, activation_order_[3]); |
| 309 EXPECT_EQ(4, browser()->tab_strip_model()->active_index()); |
| 310 } |
| 311 |
| 312 // Confirm that we get the last tab made active when we skip tabs. |
| 313 // These tests have 5 total tabs. We will only received scroll events |
| 314 // on tabs 0, 2 and 4. |
| 315 IN_PROC_BROWSER_TEST_F(TabScrubberTest, Skipped) { |
| 316 AddTabs(browser(), 4); |
| 317 |
| 318 Scrub(browser(), 0, SKIP_TABS); |
| 319 EXPECT_EQ(2U, activation_order_.size()); |
| 320 EXPECT_EQ(2, activation_order_[0]); |
| 321 EXPECT_EQ(0, activation_order_[1]); |
| 322 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 323 |
| 324 Scrub(browser(), 4, SKIP_TABS); |
| 325 EXPECT_EQ(2U, activation_order_.size()); |
| 326 EXPECT_EQ(2, activation_order_[0]); |
| 327 EXPECT_EQ(4, activation_order_[1]); |
| 328 EXPECT_EQ(4, browser()->tab_strip_model()->active_index()); |
| 329 } |
| 330 |
| 331 // Confirm that nothing happens when the swipe is small. |
| 332 IN_PROC_BROWSER_TEST_F(TabScrubberTest, NoChange) { |
| 333 AddTabs(browser(), 1); |
| 334 |
| 335 SendScrubSequence(browser(), -1, 1); |
| 336 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); |
| 337 |
| 338 SendScrubSequence(browser(), 1, 1); |
| 339 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); |
| 340 } |
| 341 |
| 342 // Confirm that very large swipes go to the beginning and and of the tabstrip. |
| 343 IN_PROC_BROWSER_TEST_F(TabScrubberTest, Bounds) { |
| 344 AddTabs(browser(), 1); |
| 345 |
| 346 SendScrubSequence(browser(), -10000, 0); |
| 347 EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); |
| 348 |
| 349 SendScrubSequence(browser(), 10000, 1); |
| 350 EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); |
| 351 } |
| 352 |
| 353 IN_PROC_BROWSER_TEST_F(TabScrubberTest, DeleteHighlighted) { |
| 354 AddTabs(browser(), 1); |
| 355 |
| 356 SendScrubEvent(browser(), 0); |
| 357 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending()); |
| 358 browser()->tab_strip_model()->CloseWebContentsAt(0, |
| 359 TabStripModel::CLOSE_NONE); |
| 360 EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending()); |
| 361 } |
| 362 |
| 363 // Delete the currently highlighted tab. Make sure the TabScrubber is aware. |
| 364 IN_PROC_BROWSER_TEST_F(TabScrubberTest, DeleteBeforeHighlighted) { |
| 365 AddTabs(browser(), 2); |
| 366 |
| 367 SendScrubEvent(browser(), 1); |
| 368 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending()); |
| 369 browser()->tab_strip_model()->CloseWebContentsAt(0, |
| 370 TabStripModel::CLOSE_NONE); |
| 371 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab()); |
| 372 } |
| 373 |
| 374 // Move the currently highlighted tab and confirm it gets tracked. |
| 375 IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveHighlighted) { |
| 376 AddTabs(browser(), 1); |
| 377 |
| 378 SendScrubEvent(browser(), 0); |
| 379 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending()); |
| 380 browser()->tab_strip_model()->ToggleSelectionAt(0); |
| 381 browser()->tab_strip_model()->ToggleSelectionAt(1); |
| 382 browser()->tab_strip_model()->MoveSelectedTabsTo(1); |
| 383 EXPECT_EQ(1, TabScrubber::GetInstance()->highlighted_tab()); |
| 384 } |
| 385 |
| 386 // Move a tab to before the highlighted one. |
| 387 IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveBefore) { |
| 388 AddTabs(browser(), 2); |
| 389 |
| 390 SendScrubEvent(browser(), 1); |
| 391 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending()); |
| 392 browser()->tab_strip_model()->ToggleSelectionAt(0); |
| 393 browser()->tab_strip_model()->ToggleSelectionAt(2); |
| 394 browser()->tab_strip_model()->MoveSelectedTabsTo(2); |
| 395 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab()); |
| 396 } |
| 397 |
| 398 // Move a tab to after the highlighted one. |
| 399 IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveAfter) { |
| 400 AddTabs(browser(), 2); |
| 401 |
| 402 SendScrubEvent(browser(), 1); |
| 403 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending()); |
| 404 browser()->tab_strip_model()->MoveSelectedTabsTo(0); |
| 405 EXPECT_EQ(2, TabScrubber::GetInstance()->highlighted_tab()); |
| 406 } |
| 407 |
| 408 #endif // OS_CHROMEOS |
OLD | NEW |