1 // Copyright 2013 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/toolbar/browser_actions_container.h" 6 7 #include "chrome/browser/chrome_notification_types.h" 8 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" 9 #include "chrome/browser/extensions/browser_action_test_util.h" 10 #include "chrome/browser/extensions/extension_browsertest.h" 11 #include "chrome/browser/extensions/extension_toolbar_model.h" 12 #include "chrome/browser/ui/browser_window.h" 13 #include "chrome/browser/ui/browser_window_testing_views.h" 14 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h" 15 #include "chrome/browser/ui/views/frame/browser_view.h" 16 #include "chrome/browser/ui/views/toolbar/browser_action_view.h" 17 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" 18 #include "content/public/test/test_utils.h" 19 #include "extensions/browser/extension_prefs.h" 20 #include "extensions/common/extension.h" 21 #include "ui/base/dragdrop/drop_target_event.h" 22 #include "ui/base/dragdrop/os_exchange_data.h" 23 #include "ui/gfx/geometry/point.h" 24 #include "ui/views/view.h" 25 26 using extensions::Extension; 27 28 class BrowserActionsContainerTest : public ExtensionBrowserTest { 29 public: 30 BrowserActionsContainerTest() { 31 } 32 virtual ~BrowserActionsContainerTest() {} 33 34 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { 35 BrowserActionsContainer::disable_animations_during_testing_ = true; 36 ExtensionBrowserTest::SetUpCommandLine(command_line); 37 } 38 39 virtual void SetUpOnMainThread() OVERRIDE { 40 ExtensionBrowserTest::SetUpOnMainThread(); 41 browser_actions_bar_.reset(new BrowserActionTestUtil(browser())); 42 } 43 44 virtual void TearDownOnMainThread() OVERRIDE { 45 BrowserActionsContainer::disable_animations_during_testing_ = false; 46 } 47 48 BrowserActionTestUtil* browser_actions_bar() { 49 return browser_actions_bar_.get(); 50 } 51 52 private: 53 scoped_ptr<BrowserActionTestUtil> browser_actions_bar_; 54 }; 55 56 // Test the basic functionality. 57 // http://crbug.com/120770 58 #if defined(OS_WIN) 59 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DISABLED_Basic) { 60 #else 61 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) { 62 #endif 63 // Load an extension with no browser action. 64 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 65 .AppendASCII("browser_action") 66 .AppendASCII("none"))); 67 // This extension should not be in the model (has no browser action). 68 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions()); 69 70 // Load an extension with a browser action. 71 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 72 .AppendASCII("browser_action") 73 .AppendASCII("basics"))); 74 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 75 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 76 77 78 // Unload the extension. 79 std::string id = browser_actions_bar()->GetExtensionId(0); 80 UnloadExtension(id); 81 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions()); 82 } 83 84 // Test moving various browser actions. This is not to check the logic of the 85 // move (that's in the toolbar model tests), but just to check our ui. 86 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, 87 MoveBrowserActions) { 88 base::FilePath data_dir = 89 test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action"); 90 // Load three extensions with browser actions. 91 const extensions::Extension* extension_a = 92 LoadExtension(data_dir.AppendASCII("basics")); 93 ASSERT_TRUE(extension_a); 94 const extensions::Extension* extension_b = 95 LoadExtension(data_dir.AppendASCII("add_popup")); 96 ASSERT_TRUE(extension_b); 97 const extensions::Extension* extension_c = 98 LoadExtension(data_dir.AppendASCII("remove_popup")); 99 ASSERT_TRUE(extension_c); 100 101 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 102 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 103 104 extensions::ExtensionToolbarModel* model = 105 extensions::ExtensionToolbarModel::Get(profile()); 106 ASSERT_TRUE(model); 107 108 // Order is now A B C. 109 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0)); 110 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 111 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2)); 112 113 // Move C to first position. Order is C A B. 114 model->MoveExtensionIcon(extension_c, 0); 115 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 116 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1)); 117 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2)); 118 119 // Move B to third position. Order is still C A B. 120 model->MoveExtensionIcon(extension_b, 2); 121 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 122 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1)); 123 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2)); 124 125 // Move B to middle position. Order is C B A. 126 model->MoveExtensionIcon(extension_b, 1); 127 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 128 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 129 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(2)); 130 } 131 132 // Test that dragging browser actions works, and that dragging a browser action 133 // from the overflow menu results in it "popping" out (growing the container 134 // size by 1), rather than just reordering the extensions. 135 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DragBrowserActions) { 136 base::FilePath data_dir = 137 test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action"); 138 // Load three extensions with browser actions. 139 const extensions::Extension* extension_a = 140 LoadExtension(data_dir.AppendASCII("basics")); 141 ASSERT_TRUE(extension_a); 142 const extensions::Extension* extension_b = 143 LoadExtension(data_dir.AppendASCII("add_popup")); 144 ASSERT_TRUE(extension_b); 145 const extensions::Extension* extension_c = 146 LoadExtension(data_dir.AppendASCII("remove_popup")); 147 ASSERT_TRUE(extension_c); 148 149 // Sanity check: All extensions showing; order is A B C. 150 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 151 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 152 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0)); 153 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 154 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2)); 155 156 BrowserActionsContainer* container = 157 BrowserView::GetBrowserViewForBrowser(browser()) 158 ->toolbar()->browser_actions(); 159 160 // Simulate a drag and drop to the right. 161 ui::OSExchangeData drop_data; 162 // Drag extension A from index 0... 163 BrowserActionDragData browser_action_drag_data(extension_a->id(), 0u); 164 browser_action_drag_data.Write(profile(), &drop_data); 165 BrowserActionView* view = container->GetViewForExtension(extension_b); 166 // ...to the right of extension B. 167 gfx::Point location(view->x() + view->width(), view->y()); 168 ui::DropTargetEvent target_event( 169 drop_data, location, location, ui::DragDropTypes::DRAG_MOVE); 170 171 // Drag and drop. 172 container->OnDragUpdated(target_event); 173 container->OnPerformDrop(target_event); 174 175 // The order should now be B A C, since A was dragged to the right of B. 176 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(0)); 177 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1)); 178 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2)); 179 180 // This order should be reflected in the underlying model. 181 extensions::ExtensionToolbarModel* model = 182 extensions::ExtensionToolbarModel::Get(profile()); 183 EXPECT_EQ(extension_b, model->toolbar_items()[0].get()); 184 EXPECT_EQ(extension_a, model->toolbar_items()[1].get()); 185 EXPECT_EQ(extension_c, model->toolbar_items()[2].get()); 186 187 // Simulate a drag and drop to the left. 188 ui::OSExchangeData drop_data2; 189 // Drag extension A from index 1... 190 BrowserActionDragData browser_action_drag_data2(extension_a->id(), 1u); 191 browser_action_drag_data2.Write(profile(), &drop_data2); 192 // ...to the left of extension B (which is now at index 0). 193 location = gfx::Point(view->x(), view->y()); 194 ui::DropTargetEvent target_event2( 195 drop_data2, location, location, ui::DragDropTypes::DRAG_MOVE); 196 197 // Drag and drop. 198 container->OnDragUpdated(target_event2); 199 container->OnPerformDrop(target_event2); 200 201 // Order should be restored to A B C. 202 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0)); 203 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 204 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2)); 205 206 // Shrink the size of the container so we have an overflow menu. 207 model->SetVisibleIconCountForTest(2u); 208 EXPECT_EQ(2u, container->VisibleBrowserActions()); 209 ASSERT_TRUE(container->chevron()); 210 EXPECT_TRUE(container->chevron()->visible()); 211 212 // Simulate a drag and drop from the overflow menu. 213 ui::OSExchangeData drop_data3; 214 // Drag extension C from index 2 (in the overflow menu)... 215 BrowserActionDragData browser_action_drag_data3(extension_c->id(), 2u); 216 browser_action_drag_data3.Write(profile(), &drop_data3); 217 // ...to the left of extension B (which is back in index 1 on the main bar). 218 location = gfx::Point(view->x(), view->y()); 219 ui::DropTargetEvent target_event3( 220 drop_data3, location, location, ui::DragDropTypes::DRAG_MOVE); 221 222 // Drag and drop. 223 container->OnDragUpdated(target_event3); 224 container->OnPerformDrop(target_event3); 225 226 // The order should have changed *and* the container should have grown to 227 // accommodate extension C. The new order should be A C B, and all three 228 // extensions should be visible, with no overflow menu. 229 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0)); 230 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(1)); 231 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2)); 232 EXPECT_EQ(3u, container->VisibleBrowserActions()); 233 EXPECT_FALSE(container->chevron()->visible()); 234 EXPECT_EQ(-1, model->GetVisibleIconCount()); 235 236 // TODO(devlin): Ideally, we'd also have tests for dragging from the legacy 237 // overflow menu (i.e., chevron) to the main bar, but this requires either 238 // having a fairly complicated interactive UI test or finding a good way to 239 // mock up the BrowserActionOverflowMenuController. 240 } 241 242 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) { 243 // Load extension A (contains browser action). 244 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 245 .AppendASCII("browser_action") 246 .AppendASCII("basics"))); 247 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 248 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 249 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 250 std::string idA = browser_actions_bar()->GetExtensionId(0); 251 252 // Load extension B (contains browser action). 253 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 254 .AppendASCII("browser_action") 255 .AppendASCII("add_popup"))); 256 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 257 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 258 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 259 std::string idB = browser_actions_bar()->GetExtensionId(1); 260 261 EXPECT_NE(idA, idB); 262 263 // Load extension C (contains browser action). 264 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 265 .AppendASCII("browser_action") 266 .AppendASCII("remove_popup"))); 267 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 268 EXPECT_TRUE(browser_actions_bar()->HasIcon(2)); 269 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 270 std::string idC = browser_actions_bar()->GetExtensionId(2); 271 272 // Change container to show only one action, rest in overflow: A, [B, C]. 273 browser_actions_bar()->SetIconVisibilityCount(1); 274 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 275 276 // Disable extension A (should disappear). State becomes: B [C]. 277 DisableExtension(idA); 278 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 279 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 280 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 281 282 // Enable A again. A should get its spot in the same location and the bar 283 // should not grow (chevron is showing). For details: http://crbug.com/35349. 284 // State becomes: A, [B, C]. 285 EnableExtension(idA); 286 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 287 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 288 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 289 290 // Disable C (in overflow). State becomes: A, [B]. 291 DisableExtension(idC); 292 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 293 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 294 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 295 296 // Enable C again. State becomes: A, [B, C]. 297 EnableExtension(idC); 298 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 299 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 300 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 301 302 // Now we have 3 extensions. Make sure they are all visible. State: A, B, C. 303 browser_actions_bar()->SetIconVisibilityCount(3); 304 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 305 306 // Disable extension A (should disappear). State becomes: B, C. 307 DisableExtension(idA); 308 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 309 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 310 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 311 312 // Disable extension B (should disappear). State becomes: C. 313 DisableExtension(idB); 314 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 315 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 316 EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0)); 317 318 // Enable B. State becomes: B, C. 319 EnableExtension(idB); 320 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 321 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 322 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 323 324 // Enable A. State becomes: A, B, C. 325 EnableExtension(idA); 326 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 327 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 328 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 329 330 // Shrink the browser actions bar to zero visible icons. 331 // No icons should be visible, but we *should* show the chevron and have a 332 // non-empty size. 333 browser_actions_bar()->SetIconVisibilityCount(0); 334 EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions()); 335 BrowserActionsContainer* container = 336 BrowserView::GetBrowserViewForBrowser(browser()) 337 ->toolbar()->browser_actions(); 338 ASSERT_TRUE(container->chevron()); 339 EXPECT_TRUE(container->chevron()->visible()); 340 EXPECT_FALSE(container->GetPreferredSize().IsEmpty()); 341 342 // Reset visibility count to 2. State should be A, B, [C], and the chevron 343 // should be visible. 344 browser_actions_bar()->SetIconVisibilityCount(2); 345 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 346 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 347 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1)); 348 EXPECT_TRUE(container->chevron()->visible()); 349 350 // Disable C (the overflowed extension). State should now be A, B, and the 351 // chevron should be hidden. 352 DisableExtension(idC); 353 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 354 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 355 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1)); 356 EXPECT_FALSE(container->chevron()->visible()); 357 358 // Re-enable C. We should still only have 2 visible icons, and the chevron 359 // should be visible. 360 EnableExtension(idC); 361 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 362 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 363 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(1)); 364 EXPECT_TRUE(container->chevron()->visible()); 365 } 366 367 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) { 368 // Load extension A (contains browser action). 369 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 370 .AppendASCII("browser_action") 371 .AppendASCII("basics"))); 372 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 373 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 374 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 375 std::string idA = browser_actions_bar()->GetExtensionId(0); 376 377 // Force hide this browser action. 378 extensions::ExtensionActionAPI::SetBrowserActionVisibility( 379 extensions::ExtensionPrefs::Get(browser()->profile()), idA, false); 380 EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions()); 381 } 382 383 // Test that the BrowserActionsContainer responds correctly when the underlying 384 // model enters highlight mode, and that browser actions are undraggable in 385 // highlight mode. (Highlight mode itself it tested more thoroughly in the 386 // ExtensionToolbarModel browsertests). 387 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, HighlightMode) { 388 // Load three extensions with browser actions. 389 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 390 .AppendASCII("browser_action") 391 .AppendASCII("basics"))); 392 std::string id_a = browser_actions_bar()->GetExtensionId(0); 393 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 394 .AppendASCII("browser_action") 395 .AppendASCII("add_popup"))); 396 std::string id_b = browser_actions_bar()->GetExtensionId(1); 397 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 398 .AppendASCII("browser_action") 399 .AppendASCII("remove_popup"))); 400 401 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 402 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 403 404 BrowserActionsContainer* container = browser() 405 ->window() 406 ->GetBrowserWindowTesting() 407 ->GetToolbarView() 408 ->browser_actions(); 409 410 // Currently, dragging should be enabled. 411 BrowserActionView* action_view = container->GetBrowserActionViewAt(0); 412 ASSERT_TRUE(action_view); 413 gfx::Point point(action_view->x(), action_view->y()); 414 EXPECT_TRUE(container->CanStartDragForView(action_view, point, point)); 415 416 extensions::ExtensionToolbarModel* model = 417 extensions::ExtensionToolbarModel::Get(profile()); 418 419 extensions::ExtensionIdList extension_ids; 420 extension_ids.push_back(id_a); 421 extension_ids.push_back(id_b); 422 model->HighlightExtensions(extension_ids); 423 424 // Only two browser actions should be visible. 425 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 426 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 427 428 // We shouldn't be able to drag in highlight mode. 429 action_view = container->GetBrowserActionViewAt(0); 430 EXPECT_FALSE(container->CanStartDragForView(action_view, point, point)); 431 432 // We should go back to normal after leaving highlight mode. 433 model->StopHighlighting(); 434 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 435 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 436 action_view = container->GetBrowserActionViewAt(0); 437 EXPECT_TRUE(container->CanStartDragForView(action_view, point, point)); 438 } 439 440 // Test the behavior of the overflow container for Extension Actions. 441 class BrowserActionsContainerOverflowTest : public BrowserActionsContainerTest { 442 public: 443 BrowserActionsContainerOverflowTest() : main_bar_(NULL), model_(NULL) { 444 } 445 virtual ~BrowserActionsContainerOverflowTest() { 446 } 447 448 protected: 449 // Returns true if the order of the BrowserActionViews in |main_bar_| 450 // and |overflow_bar_| match. 451 bool ViewOrdersMatch(); 452 453 // Returns Success if the visible count matches |expected_visible|. This means 454 // that the number of visible browser actions in |main_bar_| is 455 // |expected_visible| and shows the first icons, and that the overflow bar 456 // shows all (and only) the remainder. 457 testing::AssertionResult VerifyVisibleCount(size_t expected_visible); 458 459 // Accessors. 460 BrowserActionsContainer* main_bar() { return main_bar_; } 461 BrowserActionsContainer* overflow_bar() { return overflow_bar_.get(); } 462 extensions::ExtensionToolbarModel* model() { return model_; } 463 464 private: 465 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE; 466 virtual void SetUpOnMainThread() OVERRIDE; 467 virtual void TearDownOnMainThread() OVERRIDE; 468 469 // The main BrowserActionsContainer (owned by the browser view). 470 BrowserActionsContainer* main_bar_; 471 472 // The overflow BrowserActionsContainer. We manufacture this so that we don't 473 // have to open the wrench menu. 474 scoped_ptr<BrowserActionsContainer> overflow_bar_; 475 476 // The associated toolbar model. 477 extensions::ExtensionToolbarModel* model_; 478 479 // Enable the feature redesign switch. 480 scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_; 481 482 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest); 483 }; 484 485 void BrowserActionsContainerOverflowTest::SetUpCommandLine( 486 base::CommandLine* command_line) { 487 BrowserActionsContainerTest::SetUpCommandLine(command_line); 488 enable_redesign_.reset(new extensions::FeatureSwitch::ScopedOverride( 489 extensions::FeatureSwitch::extension_action_redesign(), 490 true)); 491 } 492 493 void BrowserActionsContainerOverflowTest::SetUpOnMainThread() { 494 BrowserActionsContainerTest::SetUpOnMainThread(); 495 main_bar_ = BrowserView::GetBrowserViewForBrowser(browser()) 496 ->toolbar()->browser_actions(); 497 overflow_bar_.reset(new BrowserActionsContainer(browser(), NULL, main_bar_)); 498 overflow_bar_->set_owned_by_client(); 499 model_ = extensions::ExtensionToolbarModel::Get(profile()); 500 } 501 502 void BrowserActionsContainerOverflowTest::TearDownOnMainThread() { 503 overflow_bar_.reset(); 504 enable_redesign_.reset(); 505 BrowserActionsContainerTest::TearDownOnMainThread(); 506 } 507 508 bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() { 509 if (main_bar_->num_browser_actions() != 510 overflow_bar_->num_browser_actions()) 511 return false; 512 for (size_t i = 0; i < main_bar_->num_browser_actions(); ++i) { 513 if (main_bar_->GetBrowserActionViewAt(i)->extension() != 514 overflow_bar_->GetBrowserActionViewAt(i)->extension()) 515 return false; 516 } 517 return true; 518 } 519 520 testing::AssertionResult 521 BrowserActionsContainerOverflowTest::VerifyVisibleCount( 522 size_t expected_visible) { 523 // Views order should always match (as it is based directly off the model). 524 if (!ViewOrdersMatch()) 525 return testing::AssertionFailure() << "View orders don't match"; 526 527 // Loop through and check each browser action for proper visibility (which 528 // implicitly also guarantees that the proper number are visible). 529 for (size_t i = 0; i < overflow_bar_->num_browser_actions(); ++i) { 530 bool visible = i < expected_visible; 531 if (main_bar_->GetBrowserActionViewAt(i)->visible() != visible) { 532 return testing::AssertionFailure() << "Index " << i << 533 " has improper visibility in main: " << !visible; 534 } 535 if (overflow_bar_->GetBrowserActionViewAt(i)->visible() == visible) { 536 return testing::AssertionFailure() << "Index " << i << 537 " has improper visibility in overflow: " << visible; 538 } 539 } 540 return testing::AssertionSuccess(); 541 } 542 543 // Test the basic functionality of the BrowserActionsContainer in overflow mode. 544 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest, 545 TestBasicActionOverflow) { 546 // Load three extensions with browser actions. 547 // TODO(devlin): Make a method to load these, and generate them rather than 548 // using files. 549 base::FilePath test_data_path = 550 test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action"); 551 const extensions::Extension* extension_a = 552 LoadExtension(test_data_path.AppendASCII("basics")); 553 const extensions::Extension* extension_b = 554 LoadExtension(test_data_path.AppendASCII("add_popup")); 555 const extensions::Extension* extension_c = 556 LoadExtension(test_data_path.AppendASCII("remove_popup")); 557 558 // Since the overflow bar isn't attached to a view, we have to kick it in 559 // order to retrigger layout each time we change the number of icons in the 560 // bar. 561 overflow_bar()->Layout(); 562 563 // Sanity checks: 564 // All extensions loaded. 565 ASSERT_TRUE(extension_a); 566 ASSERT_TRUE(extension_b); 567 ASSERT_TRUE(extension_c); 568 569 // All actions are showing, and are in the installation order. 570 EXPECT_EQ(-1, model()->GetVisibleIconCount()); 571 ASSERT_EQ(3u, main_bar()->num_browser_actions()); 572 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 573 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 574 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 575 EXPECT_TRUE(VerifyVisibleCount(3u)); 576 577 // Reduce the visible count to 2. Order should be unchanged (A B C), but 578 // only A and B should be visible on the main bar. 579 model()->SetVisibleIconCountForTest(2u); 580 overflow_bar()->Layout(); // Kick. 581 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 582 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 583 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 584 EXPECT_TRUE(VerifyVisibleCount(2u)); 585 586 // Move extension C to the first position. Order should now be C A B, with 587 // C and A visible in the main bar. 588 model()->MoveExtensionIcon(extension_c, 0); 589 overflow_bar()->Layout(); // Kick. 590 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension()); 591 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension()); 592 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension()); 593 EXPECT_TRUE(VerifyVisibleCount(2u)); 594 595 // Hide action A. This results in it being sent to overflow, and reducing the 596 // visible size to 1, so the order should be C A B, with only C visible in the 597 // main bar. 598 extensions::ExtensionActionAPI::SetBrowserActionVisibility( 599 extensions::ExtensionPrefs::Get(profile()), 600 extension_a->id(), 601 false); 602 overflow_bar()->Layout(); // Kick. 603 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension()); 604 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension()); 605 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension()); 606 EXPECT_TRUE(VerifyVisibleCount(1u)); 607 } 608 609 // Test drag and drop between the overflow container and the main container. 610 IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest, 611 TestOverflowDragging) { 612 base::FilePath test_data_path = 613 test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action"); 614 const extensions::Extension* extension_a = 615 LoadExtension(test_data_path.AppendASCII("basics")); 616 const extensions::Extension* extension_b = 617 LoadExtension(test_data_path.AppendASCII("add_popup")); 618 const extensions::Extension* extension_c = 619 LoadExtension(test_data_path.AppendASCII("remove_popup")); 620 621 // Start with one extension in overflow. 622 model()->SetVisibleIconCountForTest(2u); 623 overflow_bar()->Layout(); 624 625 // Verify starting state is A B [C]. 626 ASSERT_EQ(3u, main_bar()->num_browser_actions()); 627 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 628 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 629 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 630 EXPECT_TRUE(VerifyVisibleCount(2u)); 631 632 // Drag extension A (on the main bar) to the left of extension C (in 633 // overflow). 634 ui::OSExchangeData drop_data; 635 BrowserActionDragData browser_action_drag_data(extension_a->id(), 0u); 636 browser_action_drag_data.Write(profile(), &drop_data); 637 BrowserActionView* view = overflow_bar()->GetViewForExtension(extension_c); 638 gfx::Point location(view->x(), view->y()); 639 ui::DropTargetEvent target_event( 640 drop_data, location, location, ui::DragDropTypes::DRAG_MOVE); 641 642 overflow_bar()->OnDragUpdated(target_event); 643 overflow_bar()->OnPerformDrop(target_event); 644 overflow_bar()->Layout(); 645 646 // Order should now be B [A C]. 647 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(0)->extension()); 648 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension()); 649 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 650 VerifyVisibleCount(1u); 651 652 // Drag extension A back from overflow to the main bar. 653 ui::OSExchangeData drop_data2; 654 BrowserActionDragData browser_action_drag_data2(extension_a->id(), 1u); 655 browser_action_drag_data2.Write(profile(), &drop_data2); 656 view = main_bar()->GetViewForExtension(extension_b); 657 location = gfx::Point(view->x(), view->y()); 658 ui::DropTargetEvent target_event2( 659 drop_data2, location, location, ui::DragDropTypes::DRAG_MOVE); 660 661 main_bar()->OnDragUpdated(target_event2); 662 main_bar()->OnPerformDrop(target_event2); 663 664 // Order should be A B [C] again. 665 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 666 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 667 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 668 VerifyVisibleCount(2u); 669 670 // Drag extension C from overflow to the main bar (before extension B). 671 ui::OSExchangeData drop_data3; 672 BrowserActionDragData browser_action_drag_data3(extension_c->id(), 2u); 673 browser_action_drag_data3.Write(profile(), &drop_data3); 674 location = gfx::Point(view->x(), view->y()); 675 ui::DropTargetEvent target_event3( 676 drop_data3, location, location, ui::DragDropTypes::DRAG_MOVE); 677 678 main_bar()->OnDragUpdated(target_event3); 679 main_bar()->OnPerformDrop(target_event3); 680 681 // Order should be A C B, and there should be no extensions in overflow. 682 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 683 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(1)->extension()); 684 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension()); 685 VerifyVisibleCount(3u); 686 } 687