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 "build/build_config.h" 6 7 #include "chrome/browser/chrome_notification_types.h" 8 #include "chrome/browser/extensions/browser_action_test_util.h" 9 #include "chrome/browser/extensions/extension_action.h" 10 #include "chrome/browser/extensions/extension_action_icon_factory.h" 11 #include "chrome/browser/extensions/extension_action_manager.h" 12 #include "chrome/browser/extensions/extension_apitest.h" 13 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/extensions/extension_tab_util.h" 15 #include "chrome/browser/extensions/extension_toolbar_model.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/ui/browser.h" 18 #include "chrome/browser/ui/browser_commands.h" 19 #include "chrome/browser/ui/browser_window.h" 20 #include "chrome/browser/ui/tabs/tab_strip_model.h" 21 #include "chrome/common/url_constants.h" 22 #include "chrome/test/base/ui_test_utils.h" 23 #include "content/public/browser/notification_service.h" 24 #include "content/public/browser/web_contents.h" 25 #include "content/public/test/browser_test_utils.h" 26 #include "extensions/browser/extension_system.h" 27 #include "extensions/common/feature_switch.h" 28 #include "grit/theme_resources.h" 29 #include "ui/base/resource/resource_bundle.h" 30 #include "ui/gfx/image/image_skia.h" 31 #include "ui/gfx/image/image_skia_operations.h" 32 #include "ui/gfx/rect.h" 33 #include "ui/gfx/size.h" 34 #include "ui/gfx/skia_util.h" 35 36 using content::WebContents; 37 38 namespace extensions { 39 namespace { 40 41 const char kEmptyImageDataError[] = 42 "The imageData property must contain an ImageData object or dictionary " 43 "of ImageData objects."; 44 const char kEmptyPathError[] = "The path property must not be empty."; 45 46 // Views implementation of browser action button will return icon whose 47 // background will be set. 48 gfx::ImageSkia AddBackgroundForViews(const gfx::ImageSkia& icon) { 49 #if defined(TOOLKIT_VIEWS) 50 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 51 gfx::ImageSkia bg = *rb.GetImageSkiaNamed(IDR_BROWSER_ACTION); 52 return gfx::ImageSkiaOperations::CreateSuperimposedImage(bg, icon); 53 #else 54 return icon; 55 #endif 56 } 57 58 bool ImagesAreEqualAtScale(const gfx::ImageSkia& i1, 59 const gfx::ImageSkia& i2, 60 float scale) { 61 SkBitmap bitmap1 = i1.GetRepresentation(scale).sk_bitmap(); 62 SkBitmap bitmap2 = i2.GetRepresentation(scale).sk_bitmap(); 63 return gfx::BitmapsAreEqual(bitmap1, bitmap2); 64 } 65 66 class BrowserActionApiTest : public ExtensionApiTest { 67 public: 68 BrowserActionApiTest() {} 69 virtual ~BrowserActionApiTest() {} 70 71 protected: 72 BrowserActionTestUtil GetBrowserActionsBar() { 73 return BrowserActionTestUtil(browser()); 74 } 75 76 bool OpenPopup(int index) { 77 ResultCatcher catcher; 78 content::WindowedNotificationObserver popup_observer( 79 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 80 content::NotificationService::AllSources()); 81 GetBrowserActionsBar().Press(index); 82 popup_observer.Wait(); 83 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); 84 return GetBrowserActionsBar().HasPopup(); 85 } 86 87 ExtensionAction* GetBrowserAction(const Extension& extension) { 88 return ExtensionActionManager::Get(browser()->profile())-> 89 GetBrowserAction(extension); 90 } 91 }; 92 93 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { 94 ASSERT_TRUE(test_server()->Start()); 95 ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; 96 const Extension* extension = GetSingleLoadedExtension(); 97 ASSERT_TRUE(extension) << message_; 98 99 // Test that there is a browser action in the toolbar. 100 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 101 102 // Tell the extension to update the browser action state. 103 ResultCatcher catcher; 104 ui_test_utils::NavigateToURL(browser(), 105 GURL(extension->GetResourceURL("update.html"))); 106 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); 107 108 // Test that we received the changes. 109 ExtensionAction* action = GetBrowserAction(*extension); 110 ASSERT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId)); 111 ASSERT_EQ("badge", action->GetBadgeText(ExtensionAction::kDefaultTabId)); 112 ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255), 113 action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); 114 115 // Simulate the browser action being clicked. 116 ui_test_utils::NavigateToURL(browser(), 117 test_server()->GetURL("files/extensions/test_file.txt")); 118 119 ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get( 120 browser()->profile()); 121 toolbar_model->ExecuteBrowserAction(extension, browser(), NULL, true); 122 123 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); 124 } 125 126 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { 127 ASSERT_TRUE(RunExtensionTest("browser_action/no_icon")) << message_; 128 const Extension* extension = GetSingleLoadedExtension(); 129 ASSERT_TRUE(extension) << message_; 130 131 #if defined (OS_MACOSX) 132 // We need this on mac so we don't loose 2x representations from browser icon 133 // in transformations gfx::ImageSkia -> NSImage -> gfx::ImageSkia. 134 std::vector<ui::ScaleFactor> supported_scale_factors; 135 supported_scale_factors.push_back(ui::SCALE_FACTOR_100P); 136 supported_scale_factors.push_back(ui::SCALE_FACTOR_200P); 137 ui::SetSupportedScaleFactors(supported_scale_factors); 138 #endif 139 140 // We should not be creating icons asynchronously, so we don't need an 141 // observer. 142 ExtensionActionIconFactory icon_factory( 143 profile(), 144 extension, 145 GetBrowserAction(*extension), 146 NULL); 147 // Test that there is a browser action in the toolbar. 148 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 149 EXPECT_TRUE(GetBrowserActionsBar().HasIcon(0)); 150 151 gfx::Image action_icon = icon_factory.GetIcon(0); 152 uint32_t action_icon_last_id = action_icon.ToSkBitmap()->getGenerationID(); 153 154 // Let's check that |GetIcon| doesn't always return bitmap with new id. 155 ASSERT_EQ(action_icon_last_id, 156 icon_factory.GetIcon(0).ToSkBitmap()->getGenerationID()); 157 158 uint32_t action_icon_current_id = 0; 159 160 ResultCatcher catcher; 161 162 // Tell the extension to update the icon using ImageData object. 163 GetBrowserActionsBar().Press(0); 164 ASSERT_TRUE(catcher.GetNextResult()); 165 166 action_icon = icon_factory.GetIcon(0); 167 168 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 169 EXPECT_GT(action_icon_current_id, action_icon_last_id); 170 action_icon_last_id = action_icon_current_id; 171 172 EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(2.0f)); 173 174 EXPECT_TRUE( 175 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 176 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 177 1.0f)); 178 179 // Tell the extension to update the icon using path. 180 GetBrowserActionsBar().Press(0); 181 ASSERT_TRUE(catcher.GetNextResult()); 182 183 action_icon = icon_factory.GetIcon(0); 184 185 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 186 EXPECT_GT(action_icon_current_id, action_icon_last_id); 187 action_icon_last_id = action_icon_current_id; 188 189 EXPECT_FALSE( 190 action_icon.ToImageSkia()->HasRepresentation(2.0f)); 191 192 EXPECT_TRUE( 193 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 194 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 195 1.0f)); 196 197 // Tell the extension to update the icon using dictionary of ImageData 198 // objects. 199 GetBrowserActionsBar().Press(0); 200 ASSERT_TRUE(catcher.GetNextResult()); 201 202 action_icon = icon_factory.GetIcon(0); 203 204 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 205 EXPECT_GT(action_icon_current_id, action_icon_last_id); 206 action_icon_last_id = action_icon_current_id; 207 208 EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(2.0f)); 209 210 EXPECT_TRUE( 211 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 212 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 213 1.0f)); 214 215 // Tell the extension to update the icon using dictionary of paths. 216 GetBrowserActionsBar().Press(0); 217 ASSERT_TRUE(catcher.GetNextResult()); 218 219 action_icon = icon_factory.GetIcon(0); 220 221 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 222 EXPECT_GT(action_icon_current_id, action_icon_last_id); 223 action_icon_last_id = action_icon_current_id; 224 225 EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(2.0f)); 226 227 EXPECT_TRUE( 228 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 229 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 230 1.0f)); 231 232 // Tell the extension to update the icon using dictionary of ImageData 233 // objects, but setting only size 19. 234 GetBrowserActionsBar().Press(0); 235 ASSERT_TRUE(catcher.GetNextResult()); 236 237 action_icon = icon_factory.GetIcon(0); 238 239 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 240 EXPECT_GT(action_icon_current_id, action_icon_last_id); 241 action_icon_last_id = action_icon_current_id; 242 243 EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(2.0f)); 244 245 EXPECT_TRUE( 246 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 247 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 248 1.0f)); 249 250 // Tell the extension to update the icon using dictionary of paths, but 251 // setting only size 19. 252 GetBrowserActionsBar().Press(0); 253 ASSERT_TRUE(catcher.GetNextResult()); 254 255 action_icon = icon_factory.GetIcon(0); 256 257 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 258 EXPECT_GT(action_icon_current_id, action_icon_last_id); 259 action_icon_last_id = action_icon_current_id; 260 261 EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(2.0f)); 262 263 EXPECT_TRUE( 264 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon.ToImageSkia()), 265 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 266 1.0f)); 267 268 // Tell the extension to update the icon using dictionary of ImageData 269 // objects, but setting only size 38. 270 GetBrowserActionsBar().Press(0); 271 ASSERT_TRUE(catcher.GetNextResult()); 272 273 action_icon = icon_factory.GetIcon(0); 274 275 const gfx::ImageSkia* action_icon_skia = action_icon.ToImageSkia(); 276 277 EXPECT_FALSE(action_icon_skia->HasRepresentation(1.0f)); 278 EXPECT_TRUE(action_icon_skia->HasRepresentation(2.0f)); 279 280 action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID(); 281 EXPECT_GT(action_icon_current_id, action_icon_last_id); 282 action_icon_last_id = action_icon_current_id; 283 284 EXPECT_TRUE(gfx::BitmapsAreEqual( 285 *action_icon.ToSkBitmap(), 286 action_icon_skia->GetRepresentation(2.0f).sk_bitmap())); 287 288 EXPECT_TRUE( 289 ImagesAreEqualAtScale(AddBackgroundForViews(*action_icon_skia), 290 *GetBrowserActionsBar().GetIcon(0).ToImageSkia(), 291 2.0f)); 292 293 // Try setting icon with empty dictionary of ImageData objects. 294 GetBrowserActionsBar().Press(0); 295 ASSERT_FALSE(catcher.GetNextResult()); 296 EXPECT_EQ(kEmptyImageDataError, catcher.message()); 297 298 // Try setting icon with empty dictionary of path objects. 299 GetBrowserActionsBar().Press(0); 300 ASSERT_FALSE(catcher.GetNextResult()); 301 EXPECT_EQ(kEmptyPathError, catcher.message()); 302 } 303 304 // This test is flaky as per http://crbug.com/74557. 305 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, 306 DISABLED_TabSpecificBrowserActionState) { 307 ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) << 308 message_; 309 const Extension* extension = GetSingleLoadedExtension(); 310 ASSERT_TRUE(extension) << message_; 311 312 // Test that there is a browser action in the toolbar and that it has an icon. 313 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 314 EXPECT_TRUE(GetBrowserActionsBar().HasIcon(0)); 315 316 // Execute the action, its title should change. 317 ResultCatcher catcher; 318 GetBrowserActionsBar().Press(0); 319 ASSERT_TRUE(catcher.GetNextResult()); 320 EXPECT_EQ("Showing icon 2", GetBrowserActionsBar().GetTooltip(0)); 321 322 // Open a new tab, the title should go back. 323 chrome::NewTab(browser()); 324 EXPECT_EQ("hi!", GetBrowserActionsBar().GetTooltip(0)); 325 326 // Go back to first tab, changed title should reappear. 327 browser()->tab_strip_model()->ActivateTabAt(0, true); 328 EXPECT_EQ("Showing icon 2", GetBrowserActionsBar().GetTooltip(0)); 329 330 // Reload that tab, default title should come back. 331 ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); 332 EXPECT_EQ("hi!", GetBrowserActionsBar().GetTooltip(0)); 333 } 334 335 // http://code.google.com/p/chromium/issues/detail?id=70829 336 // Mac used to be ok, but then mac 10.5 started failing too. =( 337 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_BrowserActionPopup) { 338 ASSERT_TRUE( 339 LoadExtension(test_data_dir_.AppendASCII("browser_action/popup"))); 340 BrowserActionTestUtil actions_bar = GetBrowserActionsBar(); 341 const Extension* extension = GetSingleLoadedExtension(); 342 ASSERT_TRUE(extension) << message_; 343 344 // The extension's popup's size grows by |growFactor| each click. 345 const int growFactor = 500; 346 gfx::Size minSize = BrowserActionTestUtil::GetMinPopupSize(); 347 gfx::Size middleSize = gfx::Size(growFactor, growFactor); 348 gfx::Size maxSize = BrowserActionTestUtil::GetMaxPopupSize(); 349 350 // Ensure that two clicks will exceed the maximum allowed size. 351 ASSERT_GT(minSize.height() + growFactor * 2, maxSize.height()); 352 ASSERT_GT(minSize.width() + growFactor * 2, maxSize.width()); 353 354 // Simulate a click on the browser action and verify the size of the resulting 355 // popup. The first one tries to be 0x0, so it should be the min values. 356 ASSERT_TRUE(OpenPopup(0)); 357 EXPECT_EQ(minSize, actions_bar.GetPopupBounds().size()); 358 EXPECT_TRUE(actions_bar.HidePopup()); 359 360 ASSERT_TRUE(OpenPopup(0)); 361 EXPECT_EQ(middleSize, actions_bar.GetPopupBounds().size()); 362 EXPECT_TRUE(actions_bar.HidePopup()); 363 364 // One more time, but this time it should be constrained by the max values. 365 ASSERT_TRUE(OpenPopup(0)); 366 EXPECT_EQ(maxSize, actions_bar.GetPopupBounds().size()); 367 EXPECT_TRUE(actions_bar.HidePopup()); 368 } 369 370 // Test that calling chrome.browserAction.setPopup() can enable and change 371 // a popup. 372 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { 373 ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_; 374 const Extension* extension = GetSingleLoadedExtension(); 375 ASSERT_TRUE(extension) << message_; 376 377 int tab_id = ExtensionTabUtil::GetTabId( 378 browser()->tab_strip_model()->GetActiveWebContents()); 379 380 ExtensionAction* browser_action = GetBrowserAction(*extension); 381 ASSERT_TRUE(browser_action) 382 << "Browser action test extension should have a browser action."; 383 384 ASSERT_FALSE(browser_action->HasPopup(tab_id)); 385 ASSERT_FALSE(browser_action->HasPopup(ExtensionAction::kDefaultTabId)); 386 387 // Simulate a click on the browser action icon. The onClicked handler 388 // will add a popup. 389 { 390 ResultCatcher catcher; 391 GetBrowserActionsBar().Press(0); 392 ASSERT_TRUE(catcher.GetNextResult()); 393 } 394 395 // The call to setPopup in background.html set a tab id, so the 396 // current tab's setting should have changed, but the default setting 397 // should not have changed. 398 ASSERT_TRUE(browser_action->HasPopup(tab_id)) 399 << "Clicking on the browser action should have caused a popup to " 400 << "be added."; 401 ASSERT_FALSE(browser_action->HasPopup(ExtensionAction::kDefaultTabId)) 402 << "Clicking on the browser action should not have set a default " 403 << "popup."; 404 405 ASSERT_STREQ("/a_popup.html", 406 browser_action->GetPopupUrl(tab_id).path().c_str()); 407 408 // Now change the popup from a_popup.html to another_popup.html by loading 409 // a page which removes the popup using chrome.browserAction.setPopup(). 410 { 411 ResultCatcher catcher; 412 ui_test_utils::NavigateToURL( 413 browser(), 414 GURL(extension->GetResourceURL("change_popup.html"))); 415 ASSERT_TRUE(catcher.GetNextResult()); 416 } 417 418 // The call to setPopup in change_popup.html did not use a tab id, 419 // so the default setting should have changed as well as the current tab. 420 ASSERT_TRUE(browser_action->HasPopup(tab_id)); 421 ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId)); 422 ASSERT_STREQ("/another_popup.html", 423 browser_action->GetPopupUrl(tab_id).path().c_str()); 424 } 425 426 // Test that calling chrome.browserAction.setPopup() can remove a popup. 427 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { 428 // Load the extension, which has a browser action with a default popup. 429 ASSERT_TRUE(RunExtensionTest("browser_action/remove_popup")) << message_; 430 const Extension* extension = GetSingleLoadedExtension(); 431 ASSERT_TRUE(extension) << message_; 432 433 int tab_id = ExtensionTabUtil::GetTabId( 434 browser()->tab_strip_model()->GetActiveWebContents()); 435 436 ExtensionAction* browser_action = GetBrowserAction(*extension); 437 ASSERT_TRUE(browser_action) 438 << "Browser action test extension should have a browser action."; 439 440 ASSERT_TRUE(browser_action->HasPopup(tab_id)) 441 << "Expect a browser action popup before the test removes it."; 442 ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId)) 443 << "Expect a browser action popup is the default for all tabs."; 444 445 // Load a page which removes the popup using chrome.browserAction.setPopup(). 446 { 447 ResultCatcher catcher; 448 ui_test_utils::NavigateToURL( 449 browser(), 450 GURL(extension->GetResourceURL("remove_popup.html"))); 451 ASSERT_TRUE(catcher.GetNextResult()); 452 } 453 454 ASSERT_FALSE(browser_action->HasPopup(tab_id)) 455 << "Browser action popup should have been removed."; 456 ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId)) 457 << "Browser action popup default should not be changed by setting " 458 << "a specific tab id."; 459 } 460 461 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { 462 ASSERT_TRUE(test_server()->Start()); 463 464 ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; 465 const Extension* extension = GetSingleLoadedExtension(); 466 ASSERT_TRUE(extension) << message_; 467 468 // Test that there is a browser action in the toolbar. 469 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 470 471 // Open an incognito window and test that the browser action isn't there by 472 // default. 473 Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); 474 Browser* incognito_browser = 475 new Browser(Browser::CreateParams(incognito_profile, 476 browser()->host_desktop_type())); 477 478 ASSERT_EQ(0, 479 BrowserActionTestUtil(incognito_browser).NumberOfBrowserActions()); 480 481 // Now enable the extension in incognito mode, and test that the browser 482 // action shows up. Note that we don't update the existing window at the 483 // moment, so we just create a new one. 484 extensions::ExtensionPrefs::Get(browser()->profile()) 485 ->SetIsIncognitoEnabled(extension->id(), true); 486 487 chrome::CloseWindow(incognito_browser); 488 incognito_browser = 489 new Browser(Browser::CreateParams(incognito_profile, 490 browser()->host_desktop_type())); 491 ASSERT_EQ(1, 492 BrowserActionTestUtil(incognito_browser).NumberOfBrowserActions()); 493 494 // TODO(mpcomplete): simulate a click and have it do the right thing in 495 // incognito. 496 } 497 498 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoDragging) { 499 ExtensionService* service = extensions::ExtensionSystem::Get( 500 browser()->profile())->extension_service(); 501 502 // The tooltips for each respective browser action. 503 const char kTooltipA[] = "Alpha"; 504 const char kTooltipB[] = "Beta"; 505 const char kTooltipC[] = "Gamma"; 506 507 const size_t size_before = service->extensions()->size(); 508 509 base::FilePath test_dir = test_data_dir_.AppendASCII("browser_action"); 510 const Extension* extension_a = InstallExtension( 511 test_dir.AppendASCII("empty_browser_action_alpha.crx"), 1); 512 const Extension* extension_b = InstallExtension( 513 test_dir.AppendASCII("empty_browser_action_beta.crx"), 1); 514 const Extension* extension_c = InstallExtension( 515 test_dir.AppendASCII("empty_browser_action_gamma.crx"), 1); 516 ASSERT_TRUE(extension_a); 517 ASSERT_TRUE(extension_b); 518 ASSERT_TRUE(extension_c); 519 520 // Test that there are 3 browser actions in the toolbar. 521 ASSERT_EQ(size_before + 3, service->extensions()->size()); 522 ASSERT_EQ(3, GetBrowserActionsBar().NumberOfBrowserActions()); 523 524 // Now enable 2 of the extensions in incognito mode, and test that the browser 525 // actions show up. 526 extensions::ExtensionPrefs* prefs = 527 extensions::ExtensionPrefs::Get(browser()->profile()); 528 prefs->SetIsIncognitoEnabled(extension_a->id(), true); 529 prefs->SetIsIncognitoEnabled(extension_c->id(), true); 530 531 Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); 532 Browser* incognito_browser = 533 new Browser(Browser::CreateParams(incognito_profile, 534 browser()->host_desktop_type())); 535 BrowserActionTestUtil incognito_bar(incognito_browser); 536 537 // Navigate just to have a tab in this window, otherwise wonky things happen. 538 ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); 539 540 ASSERT_EQ(2, incognito_bar.NumberOfBrowserActions()); 541 542 // Ensure that the browser actions are in the right order (ABC). 543 EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(0)); 544 EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(1)); 545 EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(2)); 546 547 EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(0)); 548 EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(1)); 549 550 // Now rearrange them and ensure that they are rearranged correctly in both 551 // regular and incognito mode. 552 553 // ABC -> CAB 554 ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get( 555 browser()->profile()); 556 toolbar_model->MoveBrowserAction(extension_c, 0); 557 558 EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(0)); 559 EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(1)); 560 EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(2)); 561 562 EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(0)); 563 EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(1)); 564 565 // CAB -> CBA 566 toolbar_model->MoveBrowserAction(extension_b, 1); 567 568 EXPECT_EQ(kTooltipC, GetBrowserActionsBar().GetTooltip(0)); 569 EXPECT_EQ(kTooltipB, GetBrowserActionsBar().GetTooltip(1)); 570 EXPECT_EQ(kTooltipA, GetBrowserActionsBar().GetTooltip(2)); 571 572 EXPECT_EQ(kTooltipC, incognito_bar.GetTooltip(0)); 573 EXPECT_EQ(kTooltipA, incognito_bar.GetTooltip(1)); 574 } 575 576 // Tests that events are dispatched to the correct profile for split mode 577 // extensions. 578 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoSplit) { 579 ResultCatcher catcher; 580 const Extension* extension = LoadExtensionWithFlags( 581 test_data_dir_.AppendASCII("browser_action/split_mode"), 582 kFlagEnableIncognito); 583 ASSERT_TRUE(extension) << message_; 584 585 // Open an incognito window. 586 Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); 587 Browser* incognito_browser = 588 new Browser(Browser::CreateParams(incognito_profile, 589 browser()->host_desktop_type())); 590 // Navigate just to have a tab in this window, otherwise wonky things happen. 591 ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); 592 ASSERT_EQ(1, 593 BrowserActionTestUtil(incognito_browser).NumberOfBrowserActions()); 594 595 // A click in the regular profile should open a tab in the regular profile. 596 ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get( 597 browser()->profile()); 598 toolbar_model->ExecuteBrowserAction(extension, browser(), NULL, true); 599 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); 600 601 // A click in the incognito profile should open a tab in the 602 // incognito profile. 603 toolbar_model->ExecuteBrowserAction(extension, incognito_browser, NULL, true); 604 ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); 605 } 606 607 // Disabled because of failures (crashes) on ASAN bot. 608 // See http://crbug.com/98861. 609 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_CloseBackgroundPage) { 610 ASSERT_TRUE(LoadExtension( 611 test_data_dir_.AppendASCII("browser_action/close_background"))); 612 const Extension* extension = GetSingleLoadedExtension(); 613 614 // There is a background page and a browser action with no badge text. 615 extensions::ProcessManager* manager = 616 extensions::ExtensionSystem::Get(browser()->profile())->process_manager(); 617 ASSERT_TRUE(manager->GetBackgroundHostForExtension(extension->id())); 618 ExtensionAction* action = GetBrowserAction(*extension); 619 ASSERT_EQ("", action->GetBadgeText(ExtensionAction::kDefaultTabId)); 620 621 content::WindowedNotificationObserver host_destroyed_observer( 622 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, 623 content::NotificationService::AllSources()); 624 625 // Click the browser action. 626 ExtensionToolbarModel* toolbar_model = ExtensionToolbarModel::Get( 627 browser()->profile()); 628 toolbar_model->ExecuteBrowserAction(extension, browser(), NULL, true); 629 630 // It can take a moment for the background page to actually get destroyed 631 // so we wait for the notification before checking that it's really gone 632 // and the badge text has been set. 633 host_destroyed_observer.Wait(); 634 ASSERT_FALSE(manager->GetBackgroundHostForExtension(extension->id())); 635 ASSERT_EQ("X", action->GetBadgeText(ExtensionAction::kDefaultTabId)); 636 } 637 638 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BadgeBackgroundColor) { 639 ASSERT_TRUE(test_server()->Start()); 640 ASSERT_TRUE(RunExtensionTest("browser_action/color")) << message_; 641 const Extension* extension = GetSingleLoadedExtension(); 642 ASSERT_TRUE(extension) << message_; 643 644 // Test that there is a browser action in the toolbar. 645 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 646 647 // Test that CSS values (#FF0000) set color correctly. 648 ExtensionAction* action = GetBrowserAction(*extension); 649 ASSERT_EQ(SkColorSetARGB(255, 255, 0, 0), 650 action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); 651 652 // Tell the extension to update the browser action state. 653 ResultCatcher catcher; 654 ui_test_utils::NavigateToURL(browser(), 655 GURL(extension->GetResourceURL("update.html"))); 656 ASSERT_TRUE(catcher.GetNextResult()); 657 658 // Test that CSS values (#0F0) set color correctly. 659 ASSERT_EQ(SkColorSetARGB(255, 0, 255, 0), 660 action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); 661 662 ui_test_utils::NavigateToURL(browser(), 663 GURL(extension->GetResourceURL("update2.html"))); 664 ASSERT_TRUE(catcher.GetNextResult()); 665 666 // Test that array values set color correctly. 667 ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255), 668 action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); 669 } 670 671 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Getters) { 672 ASSERT_TRUE(RunExtensionTest("browser_action/getters")) << message_; 673 const Extension* extension = GetSingleLoadedExtension(); 674 ASSERT_TRUE(extension) << message_; 675 676 // Test that there is a browser action in the toolbar. 677 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 678 679 // Test the getters for defaults. 680 ResultCatcher catcher; 681 ui_test_utils::NavigateToURL(browser(), 682 GURL(extension->GetResourceURL("update.html"))); 683 ASSERT_TRUE(catcher.GetNextResult()); 684 685 // Test the getters for a specific tab. 686 ui_test_utils::NavigateToURL(browser(), 687 GURL(extension->GetResourceURL("update2.html"))); 688 ASSERT_TRUE(catcher.GetNextResult()); 689 } 690 691 // Verify triggering browser action. 692 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TestTriggerBrowserAction) { 693 ASSERT_TRUE(test_server()->Start()); 694 695 ASSERT_TRUE(RunExtensionTest("trigger_actions/browser_action")) << message_; 696 const Extension* extension = GetSingleLoadedExtension(); 697 ASSERT_TRUE(extension) << message_; 698 699 // Test that there is a browser action in the toolbar. 700 ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); 701 702 ui_test_utils::NavigateToURL( 703 browser(), 704 test_server()->GetURL("files/simple.html")); 705 706 ExtensionAction* browser_action = GetBrowserAction(*extension); 707 EXPECT_TRUE(browser_action != NULL); 708 709 // Simulate a click on the browser action icon. 710 { 711 ResultCatcher catcher; 712 GetBrowserActionsBar().Press(0); 713 EXPECT_TRUE(catcher.GetNextResult()); 714 } 715 716 WebContents* tab = 717 browser()->tab_strip_model()->GetActiveWebContents(); 718 EXPECT_TRUE(tab != NULL); 719 720 // Verify that the browser action turned the background color red. 721 const std::string script = 722 "window.domAutomationController.send(document.body.style." 723 "backgroundColor);"; 724 std::string result; 725 EXPECT_TRUE(content::ExecuteScriptAndExtractString(tab, script, &result)); 726 EXPECT_EQ(result, "red"); 727 } 728 729 } // namespace 730 } // namespace extensions 731