browser_actions_container_browsertest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/frame/browser_view.h" 15#include "chrome/browser/ui/views/toolbar/browser_action_view.h" 16#include "chrome/browser/ui/views/toolbar/toolbar_view.h" 17#include "content/public/test/test_utils.h" 18#include "extensions/browser/extension_prefs.h" 19#include "extensions/common/extension.h" 20#include "ui/gfx/geometry/point.h" 21#include "ui/views/view.h" 22 23using extensions::Extension; 24 25class BrowserActionsContainerTest : public ExtensionBrowserTest { 26 public: 27 BrowserActionsContainerTest() { 28 } 29 virtual ~BrowserActionsContainerTest() {} 30 31 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { 32 BrowserActionsContainer::disable_animations_during_testing_ = true; 33 ExtensionBrowserTest::SetUpCommandLine(command_line); 34 } 35 36 virtual void SetUpOnMainThread() OVERRIDE { 37 ExtensionBrowserTest::SetUpOnMainThread(); 38 browser_actions_bar_.reset(new BrowserActionTestUtil(browser())); 39 } 40 41 virtual void TearDownOnMainThread() OVERRIDE { 42 BrowserActionsContainer::disable_animations_during_testing_ = false; 43 } 44 45 BrowserActionTestUtil* browser_actions_bar() { 46 return browser_actions_bar_.get(); 47 } 48 49 private: 50 scoped_ptr<BrowserActionTestUtil> browser_actions_bar_; 51}; 52 53// Test the basic functionality. 54// http://crbug.com/120770 55#if defined(OS_WIN) 56IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DISABLED_Basic) { 57#else 58IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) { 59#endif 60 // Load an extension with no browser action. 61 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 62 .AppendASCII("browser_action") 63 .AppendASCII("none"))); 64 // This extension should not be in the model (has no browser action). 65 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions()); 66 67 // Load an extension with a browser action. 68 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 69 .AppendASCII("browser_action") 70 .AppendASCII("basics"))); 71 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 72 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 73 74 75 // Unload the extension. 76 std::string id = browser_actions_bar()->GetExtensionId(0); 77 UnloadExtension(id); 78 EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions()); 79} 80 81// Test moving various browser actions. This is not to check the logic of the 82// move (that's in the toolbar model tests), but just to check our ui. 83IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, 84 MoveBrowserActions) { 85 // Load three extensions with browser actions. 86 const extensions::Extension* extension_a = 87 LoadExtension(test_data_dir_.AppendASCII("api_test") 88 .AppendASCII("browser_action") 89 .AppendASCII("basics")); 90 ASSERT_TRUE(extension_a); 91 const extensions::Extension* extension_b = 92 LoadExtension(test_data_dir_.AppendASCII("api_test") 93 .AppendASCII("browser_action") 94 .AppendASCII("add_popup")); 95 ASSERT_TRUE(extension_b); 96 const extensions::Extension* extension_c = 97 LoadExtension(test_data_dir_.AppendASCII("api_test") 98 .AppendASCII("browser_action") 99 .AppendASCII("remove_popup")); 100 ASSERT_TRUE(extension_c); 101 102 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 103 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 104 105 extensions::ExtensionToolbarModel* model = 106 extensions::ExtensionToolbarModel::Get(profile()); 107 ASSERT_TRUE(model); 108 109 // Order is now A B C. 110 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(0)); 111 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 112 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(2)); 113 114 // Move C to first position. Order is C A B. 115 model->MoveExtensionIcon(extension_c, 0); 116 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 117 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1)); 118 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2)); 119 120 // Move B to third position. Order is still C A B. 121 model->MoveExtensionIcon(extension_b, 2); 122 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 123 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(1)); 124 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(2)); 125 126 // Move B to middle position. Order is C B A. 127 model->MoveExtensionIcon(extension_b, 1); 128 EXPECT_EQ(extension_c->id(), browser_actions_bar()->GetExtensionId(0)); 129 EXPECT_EQ(extension_b->id(), browser_actions_bar()->GetExtensionId(1)); 130 EXPECT_EQ(extension_a->id(), browser_actions_bar()->GetExtensionId(2)); 131} 132 133IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) { 134 // Load extension A (contains browser action). 135 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 136 .AppendASCII("browser_action") 137 .AppendASCII("basics"))); 138 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 139 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 140 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 141 std::string idA = browser_actions_bar()->GetExtensionId(0); 142 143 // Load extension B (contains browser action). 144 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 145 .AppendASCII("browser_action") 146 .AppendASCII("add_popup"))); 147 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 148 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 149 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 150 std::string idB = browser_actions_bar()->GetExtensionId(1); 151 152 EXPECT_NE(idA, idB); 153 154 // Load extension C (contains browser action). 155 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 156 .AppendASCII("browser_action") 157 .AppendASCII("remove_popup"))); 158 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 159 EXPECT_TRUE(browser_actions_bar()->HasIcon(2)); 160 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 161 std::string idC = browser_actions_bar()->GetExtensionId(2); 162 163 // Change container to show only one action, rest in overflow: A, [B, C]. 164 browser_actions_bar()->SetIconVisibilityCount(1); 165 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 166 167 // Disable extension A (should disappear). State becomes: B [C]. 168 DisableExtension(idA); 169 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 170 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 171 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 172 173 // Enable A again. A should get its spot in the same location and the bar 174 // should not grow (chevron is showing). For details: http://crbug.com/35349. 175 // State becomes: A, [B, C]. 176 EnableExtension(idA); 177 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 178 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 179 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 180 181 // Disable C (in overflow). State becomes: A, [B]. 182 DisableExtension(idC); 183 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 184 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 185 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 186 187 // Enable C again. State becomes: A, [B, C]. 188 EnableExtension(idC); 189 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 190 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 191 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 192 193 // Now we have 3 extensions. Make sure they are all visible. State: A, B, C. 194 browser_actions_bar()->SetIconVisibilityCount(3); 195 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 196 197 // Disable extension A (should disappear). State becomes: B, C. 198 DisableExtension(idA); 199 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 200 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 201 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 202 203 // Disable extension B (should disappear). State becomes: C. 204 DisableExtension(idB); 205 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 206 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 207 EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0)); 208 209 // Enable B. State becomes: B, C. 210 EnableExtension(idB); 211 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 212 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 213 EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0)); 214 215 // Enable A. State becomes: A, B, C. 216 EnableExtension(idA); 217 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 218 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 219 EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0)); 220} 221 222IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) { 223 // Load extension A (contains browser action). 224 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 225 .AppendASCII("browser_action") 226 .AppendASCII("basics"))); 227 EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions()); 228 EXPECT_TRUE(browser_actions_bar()->HasIcon(0)); 229 EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions()); 230 std::string idA = browser_actions_bar()->GetExtensionId(0); 231 232 // Force hide this browser action. 233 extensions::ExtensionActionAPI::SetBrowserActionVisibility( 234 extensions::ExtensionPrefs::Get(browser()->profile()), idA, false); 235 EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions()); 236} 237 238// Test that the BrowserActionsContainer responds correctly when the underlying 239// model enters highlight mode, and that browser actions are undraggable in 240// highlight mode. (Highlight mode itself it tested more thoroughly in the 241// ExtensionToolbarModel browsertests). 242IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, HighlightMode) { 243 // Load three extensions with browser actions. 244 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 245 .AppendASCII("browser_action") 246 .AppendASCII("basics"))); 247 std::string id_a = browser_actions_bar()->GetExtensionId(0); 248 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 249 .AppendASCII("browser_action") 250 .AppendASCII("add_popup"))); 251 std::string id_b = browser_actions_bar()->GetExtensionId(1); 252 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 253 .AppendASCII("browser_action") 254 .AppendASCII("remove_popup"))); 255 256 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 257 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 258 259 BrowserActionsContainer* container = browser() 260 ->window() 261 ->GetBrowserWindowTesting() 262 ->GetToolbarView() 263 ->browser_actions(); 264 265 // Currently, dragging should be enabled. 266 BrowserActionView* action_view = container->GetBrowserActionViewAt(0); 267 ASSERT_TRUE(action_view); 268 gfx::Point point(action_view->x(), action_view->y()); 269 EXPECT_TRUE(container->CanStartDragForView(action_view, point, point)); 270 271 extensions::ExtensionToolbarModel* model = 272 extensions::ExtensionToolbarModel::Get(profile()); 273 274 extensions::ExtensionIdList extension_ids; 275 extension_ids.push_back(id_a); 276 extension_ids.push_back(id_b); 277 model->HighlightExtensions(extension_ids); 278 279 // Only two browser actions should be visible. 280 EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions()); 281 EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions()); 282 283 // We shouldn't be able to drag in highlight mode. 284 action_view = container->GetBrowserActionViewAt(0); 285 EXPECT_FALSE(container->CanStartDragForView(action_view, point, point)); 286 287 // We should go back to normal after leaving highlight mode. 288 model->StopHighlighting(); 289 EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions()); 290 EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions()); 291 action_view = container->GetBrowserActionViewAt(0); 292 EXPECT_TRUE(container->CanStartDragForView(action_view, point, point)); 293} 294 295// Test the behavior of the overflow container for Extension Actions. 296class BrowserActionsContainerOverflowTest : public BrowserActionsContainerTest { 297 public: 298 BrowserActionsContainerOverflowTest() : main_bar_(NULL), model_(NULL) { 299 } 300 virtual ~BrowserActionsContainerOverflowTest() { 301 } 302 303 protected: 304 // Returns true if the order of the BrowserActionViews in |main_bar_| 305 // and |overflow_bar_| match. 306 bool ViewOrdersMatch(); 307 308 // Returns Success if the visible count matches |expected_visible|. This means 309 // that the number of visible browser actions in |main_bar_| is 310 // |expected_visible| and shows the first icons, and that the overflow bar 311 // shows all (and only) the remainder. 312 testing::AssertionResult VerifyVisibleCount(size_t expected_visible); 313 314 // Accessors. 315 BrowserActionsContainer* main_bar() { return main_bar_; } 316 BrowserActionsContainer* overflow_bar() { return overflow_bar_.get(); } 317 extensions::ExtensionToolbarModel* model() { return model_; } 318 319 private: 320 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE; 321 virtual void SetUpOnMainThread() OVERRIDE; 322 virtual void TearDownOnMainThread() OVERRIDE; 323 324 // The main BrowserActionsContainer (owned by the browser view). 325 BrowserActionsContainer* main_bar_; 326 327 // The overflow BrowserActionsContainer. We manufacture this so that we don't 328 // have to open the wrench menu. 329 scoped_ptr<BrowserActionsContainer> overflow_bar_; 330 331 // The associated toolbar model. 332 extensions::ExtensionToolbarModel* model_; 333 334 // Enable the feature redesign switch. 335 scoped_ptr<extensions::FeatureSwitch::ScopedOverride> enable_redesign_; 336 337 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerOverflowTest); 338}; 339 340void BrowserActionsContainerOverflowTest::SetUpCommandLine( 341 base::CommandLine* command_line) { 342 BrowserActionsContainerTest::SetUpCommandLine(command_line); 343 enable_redesign_.reset(new extensions::FeatureSwitch::ScopedOverride( 344 extensions::FeatureSwitch::extension_action_redesign(), 345 true)); 346} 347 348void BrowserActionsContainerOverflowTest::SetUpOnMainThread() { 349 BrowserActionsContainerTest::SetUpOnMainThread(); 350 main_bar_ = BrowserView::GetBrowserViewForBrowser(browser()) 351 ->toolbar()->browser_actions(); 352 overflow_bar_.reset(new BrowserActionsContainer(browser(), NULL, main_bar_)); 353 overflow_bar_->set_owned_by_client(); 354 model_ = extensions::ExtensionToolbarModel::Get(profile()); 355} 356 357void BrowserActionsContainerOverflowTest::TearDownOnMainThread() { 358 overflow_bar_.reset(); 359 enable_redesign_.reset(); 360 BrowserActionsContainerTest::TearDownOnMainThread(); 361} 362 363bool BrowserActionsContainerOverflowTest::ViewOrdersMatch() { 364 if (main_bar_->num_browser_actions() != 365 overflow_bar_->num_browser_actions()) 366 return false; 367 for (size_t i = 0; i < main_bar_->num_browser_actions(); ++i) { 368 if (main_bar_->GetBrowserActionViewAt(i)->extension() != 369 overflow_bar_->GetBrowserActionViewAt(i)->extension()) 370 return false; 371 } 372 return true; 373} 374 375testing::AssertionResult 376BrowserActionsContainerOverflowTest::VerifyVisibleCount( 377 size_t expected_visible) { 378 // Views order should always match (as it is based directly off the model). 379 if (!ViewOrdersMatch()) 380 return testing::AssertionFailure() << "View orders don't match"; 381 382 // Loop through and check each browser action for proper visibility (which 383 // implicitly also guarantees that the proper number are visible). 384 for (size_t i = 0; i < overflow_bar_->num_browser_actions(); ++i) { 385 bool visible = i < expected_visible; 386 if (main_bar_->GetBrowserActionViewAt(i)->visible() != visible) { 387 return testing::AssertionFailure() << "Index " << i << 388 " has improper visibility in main: " << !visible; 389 } 390 if (overflow_bar_->GetBrowserActionViewAt(i)->visible() == visible) { 391 return testing::AssertionFailure() << "Index " << i << 392 " has improper visibility in overflow: " << visible; 393 } 394 } 395 return testing::AssertionSuccess(); 396} 397 398// Test the basic functionality of the BrowserActionsContainer in overflow mode. 399IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest, 400 TestBasicActionOverflow) { 401 // Load three extensions with browser actions. 402 // TODO(devlin): Make a method to load these, and generate them rather than 403 // using files. 404 base::FilePath test_data_path = 405 test_data_dir_.AppendASCII("api_test").AppendASCII("browser_action"); 406 const extensions::Extension* extension_a = 407 LoadExtension(test_data_path.AppendASCII("basics")); 408 const extensions::Extension* extension_b = 409 LoadExtension(test_data_path.AppendASCII("add_popup")); 410 const extensions::Extension* extension_c = 411 LoadExtension(test_data_path.AppendASCII("remove_popup")); 412 413 // Since the overflow bar isn't attached to a view, we have to kick it in 414 // order to retrigger layout each time we change the number of icons in the 415 // bar. 416 overflow_bar()->Layout(); 417 418 // Sanity checks: 419 // All extensions loaded. 420 ASSERT_TRUE(extension_a); 421 ASSERT_TRUE(extension_b); 422 ASSERT_TRUE(extension_c); 423 424 // All actions are showing, and are in the installation order. 425 EXPECT_EQ(-1, model()->GetVisibleIconCount()); 426 ASSERT_EQ(3u, main_bar()->num_browser_actions()); 427 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 428 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 429 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 430 EXPECT_TRUE(VerifyVisibleCount(3u)); 431 432 // Reduce the visible count to 2. Order should be unchanged (A B C), but 433 // only A and B should be visible on the main bar. 434 model()->SetVisibleIconCountForTest(2u); 435 overflow_bar()->Layout(); // Kick. 436 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(0)->extension()); 437 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(1)->extension()); 438 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(2)->extension()); 439 EXPECT_TRUE(VerifyVisibleCount(2u)); 440 441 // Move extension C to the first position. Order should now be C A B, with 442 // C and A visible in the main bar. 443 model()->MoveExtensionIcon(extension_c, 0); 444 overflow_bar()->Layout(); // Kick. 445 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension()); 446 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension()); 447 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension()); 448 EXPECT_TRUE(VerifyVisibleCount(2u)); 449 450 // Hide action A. This results in it being sent to overflow, and reducing the 451 // visible size to 1, so the order should be C A B, with only C visible in the 452 // main bar. 453 extensions::ExtensionActionAPI::SetBrowserActionVisibility( 454 extensions::ExtensionPrefs::Get(profile()), 455 extension_a->id(), 456 false); 457 overflow_bar()->Layout(); // Kick. 458 EXPECT_EQ(extension_c, main_bar()->GetBrowserActionViewAt(0)->extension()); 459 EXPECT_EQ(extension_a, main_bar()->GetBrowserActionViewAt(1)->extension()); 460 EXPECT_EQ(extension_b, main_bar()->GetBrowserActionViewAt(2)->extension()); 461 EXPECT_TRUE(VerifyVisibleCount(1u)); 462} 463