1 // Copyright (c) 2011 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/extensions/extension_browsertest.h" 6 #include "chrome/browser/extensions/extension_service.h" 7 #include "chrome/browser/extensions/extension_system.h" 8 #include "chrome/browser/extensions/extension_toolbar_model.h" 9 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/ui/browser.h" 11 #include "chrome/test/base/in_process_browser_test.h" 12 13 using extensions::Extension; 14 15 // An InProcessBrowserTest for testing the ExtensionToolbarModel. 16 // TODO(erikkay) It's unfortunate that this needs to be an in-proc browser test. 17 // It would be nice to refactor things so that ExtensionService could run 18 // without so much of the browser in place. 19 class ExtensionToolbarModelTest : public ExtensionBrowserTest, 20 public ExtensionToolbarModel::Observer { 21 public: 22 virtual void SetUp() { 23 inserted_count_ = 0; 24 removed_count_ = 0; 25 moved_count_ = 0; 26 27 ExtensionBrowserTest::SetUp(); 28 } 29 30 virtual void SetUpOnMainThread() OVERRIDE { 31 ExtensionService* service = extensions::ExtensionSystem::Get( 32 browser()->profile())->extension_service(); 33 model_ = service->toolbar_model(); 34 model_->AddObserver(this); 35 } 36 37 virtual void CleanUpOnMainThread() OVERRIDE { 38 model_->RemoveObserver(this); 39 } 40 41 virtual void BrowserActionAdded(const Extension* extension, 42 int index) OVERRIDE { 43 inserted_count_++; 44 } 45 46 virtual void BrowserActionRemoved(const Extension* extension) OVERRIDE { 47 removed_count_++; 48 } 49 50 virtual void BrowserActionMoved(const Extension* extension, 51 int index) OVERRIDE { 52 moved_count_++; 53 } 54 55 const Extension* ExtensionAt(int index) { 56 const extensions::ExtensionList& toolbar_items = model_->toolbar_items(); 57 for (extensions::ExtensionList::const_iterator i = toolbar_items.begin(); 58 i < toolbar_items.end(); ++i) { 59 if (index-- == 0) 60 return i->get(); 61 } 62 return NULL; 63 } 64 65 protected: 66 ExtensionToolbarModel* model_; 67 68 int inserted_count_; 69 int removed_count_; 70 int moved_count_; 71 }; 72 73 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) { 74 // Load an extension with no browser action. 75 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 76 .AppendASCII("browser_action") 77 .AppendASCII("none"))); 78 79 // This extension should not be in the model (has no browser action). 80 EXPECT_EQ(0, inserted_count_); 81 EXPECT_EQ(0u, model_->toolbar_items().size()); 82 ASSERT_EQ(NULL, ExtensionAt(0)); 83 84 // Load an extension with a browser action. 85 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test") 86 .AppendASCII("browser_action") 87 .AppendASCII("basics"))); 88 89 // We should now find our extension in the model. 90 EXPECT_EQ(1, inserted_count_); 91 EXPECT_EQ(1u, model_->toolbar_items().size()); 92 const Extension* extension = ExtensionAt(0); 93 ASSERT_TRUE(NULL != extension); 94 EXPECT_STREQ("A browser action with no icon that makes the page red", 95 extension->name().c_str()); 96 97 // Should be a no-op, but still fires the events. 98 model_->MoveBrowserAction(extension, 0); 99 EXPECT_EQ(1, moved_count_); 100 EXPECT_EQ(1u, model_->toolbar_items().size()); 101 const Extension* extension2 = ExtensionAt(0); 102 EXPECT_EQ(extension, extension2); 103 104 UnloadExtension(extension->id()); 105 EXPECT_EQ(1, removed_count_); 106 EXPECT_EQ(0u, model_->toolbar_items().size()); 107 EXPECT_EQ(NULL, ExtensionAt(0)); 108 } 109 110 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) { 111 // Load an extension with a browser action. 112 base::FilePath extension_a_path(test_data_dir_.AppendASCII("api_test") 113 .AppendASCII("browser_action") 114 .AppendASCII("basics")); 115 ASSERT_TRUE(LoadExtension(extension_a_path)); 116 117 // First extension loaded. 118 EXPECT_EQ(1, inserted_count_); 119 EXPECT_EQ(1u, model_->toolbar_items().size()); 120 const Extension* extensionA = ExtensionAt(0); 121 ASSERT_TRUE(NULL != extensionA); 122 EXPECT_STREQ("A browser action with no icon that makes the page red", 123 extensionA->name().c_str()); 124 125 // Load another extension with a browser action. 126 base::FilePath extension_b_path(test_data_dir_.AppendASCII("api_test") 127 .AppendASCII("browser_action") 128 .AppendASCII("popup")); 129 ASSERT_TRUE(LoadExtension(extension_b_path)); 130 131 // Second extension loaded. 132 EXPECT_EQ(2, inserted_count_); 133 EXPECT_EQ(2u, model_->toolbar_items().size()); 134 const Extension* extensionB = ExtensionAt(1); 135 ASSERT_TRUE(NULL != extensionB); 136 EXPECT_STREQ("Popup tester", extensionB->name().c_str()); 137 138 // Load yet another extension with a browser action. 139 base::FilePath extension_c_path(test_data_dir_.AppendASCII("api_test") 140 .AppendASCII("browser_action") 141 .AppendASCII("remove_popup")); 142 ASSERT_TRUE(LoadExtension(extension_c_path)); 143 144 // Third extension loaded. 145 EXPECT_EQ(3, inserted_count_); 146 EXPECT_EQ(3u, model_->toolbar_items().size()); 147 const Extension* extensionC = ExtensionAt(2); 148 ASSERT_TRUE(NULL != extensionC); 149 EXPECT_STREQ("A page action which removes a popup.", 150 extensionC->name().c_str()); 151 152 // Order is now A, B, C. Let's put C first. 153 model_->MoveBrowserAction(extensionC, 0); 154 EXPECT_EQ(1, moved_count_); 155 EXPECT_EQ(3u, model_->toolbar_items().size()); 156 EXPECT_EQ(extensionC, ExtensionAt(0)); 157 EXPECT_EQ(extensionA, ExtensionAt(1)); 158 EXPECT_EQ(extensionB, ExtensionAt(2)); 159 EXPECT_EQ(NULL, ExtensionAt(3)); 160 161 // Order is now C, A, B. Let's put A last. 162 model_->MoveBrowserAction(extensionA, 2); 163 EXPECT_EQ(2, moved_count_); 164 EXPECT_EQ(3u, model_->toolbar_items().size()); 165 EXPECT_EQ(extensionC, ExtensionAt(0)); 166 EXPECT_EQ(extensionB, ExtensionAt(1)); 167 EXPECT_EQ(extensionA, ExtensionAt(2)); 168 EXPECT_EQ(NULL, ExtensionAt(3)); 169 170 // Order is now C, B, A. Let's remove B. 171 std::string idB = extensionB->id(); 172 UnloadExtension(idB); 173 EXPECT_EQ(1, removed_count_); 174 EXPECT_EQ(2u, model_->toolbar_items().size()); 175 EXPECT_EQ(extensionC, ExtensionAt(0)); 176 EXPECT_EQ(extensionA, ExtensionAt(1)); 177 EXPECT_EQ(NULL, ExtensionAt(2)); 178 179 // Load extension B again. 180 ASSERT_TRUE(LoadExtension(extension_b_path)); 181 182 // Extension B loaded again. 183 EXPECT_EQ(4, inserted_count_); 184 EXPECT_EQ(3u, model_->toolbar_items().size()); 185 // Make sure it gets its old spot in the list. We should get the same 186 // extension again, otherwise the order has changed. 187 ASSERT_STREQ(idB.c_str(), ExtensionAt(1)->id().c_str()); 188 189 // Unload B again. 190 UnloadExtension(idB); 191 EXPECT_EQ(2, removed_count_); 192 EXPECT_EQ(2u, model_->toolbar_items().size()); 193 EXPECT_EQ(extensionC, ExtensionAt(0)); 194 EXPECT_EQ(extensionA, ExtensionAt(1)); 195 EXPECT_EQ(NULL, ExtensionAt(2)); 196 197 // Order is now C, A. Flip it. 198 model_->MoveBrowserAction(extensionA, 0); 199 EXPECT_EQ(3, moved_count_); 200 EXPECT_EQ(2u, model_->toolbar_items().size()); 201 EXPECT_EQ(extensionA, ExtensionAt(0)); 202 EXPECT_EQ(extensionC, ExtensionAt(1)); 203 EXPECT_EQ(NULL, ExtensionAt(2)); 204 205 // Move A to the location it already occupies. 206 model_->MoveBrowserAction(extensionA, 0); 207 EXPECT_EQ(4, moved_count_); 208 EXPECT_EQ(2u, model_->toolbar_items().size()); 209 EXPECT_EQ(extensionA, ExtensionAt(0)); 210 EXPECT_EQ(extensionC, ExtensionAt(1)); 211 EXPECT_EQ(NULL, ExtensionAt(2)); 212 213 // Order is now A, C. Remove C. 214 std::string idC = extensionC->id(); 215 UnloadExtension(idC); 216 EXPECT_EQ(3, removed_count_); 217 EXPECT_EQ(1u, model_->toolbar_items().size()); 218 EXPECT_EQ(extensionA, ExtensionAt(0)); 219 EXPECT_EQ(NULL, ExtensionAt(1)); 220 221 // Load extension C again. 222 ASSERT_TRUE(LoadExtension(extension_c_path)); 223 224 // Extension C loaded again. 225 EXPECT_EQ(5, inserted_count_); 226 EXPECT_EQ(2u, model_->toolbar_items().size()); 227 // Make sure it gets its old spot in the list (at the very end). 228 ASSERT_STREQ(idC.c_str(), ExtensionAt(1)->id().c_str()); 229 } 230 231 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, UnloadAndDisableMultiple) { 232 // Load three extensions with browser action. 233 base::FilePath extension_a_path(test_data_dir_.AppendASCII("api_test") 234 .AppendASCII("browser_action") 235 .AppendASCII("basics")); 236 ASSERT_TRUE(LoadExtension(extension_a_path)); 237 base::FilePath extension_b_path(test_data_dir_.AppendASCII("api_test") 238 .AppendASCII("browser_action") 239 .AppendASCII("popup")); 240 ASSERT_TRUE(LoadExtension(extension_b_path)); 241 base::FilePath extension_c_path(test_data_dir_.AppendASCII("api_test") 242 .AppendASCII("browser_action") 243 .AppendASCII("remove_popup")); 244 ASSERT_TRUE(LoadExtension(extension_c_path)); 245 246 // Verify we got the three we asked for and that they are ordered as: A, B, C. 247 const Extension* extensionA = ExtensionAt(0); 248 const Extension* extensionB = ExtensionAt(1); 249 const Extension* extensionC = ExtensionAt(2); 250 std::string idA = extensionA->id(); 251 std::string idB = extensionB->id(); 252 std::string idC = extensionC->id(); 253 EXPECT_STREQ("A browser action with no icon that makes the page red", 254 extensionA->name().c_str()); 255 EXPECT_STREQ("Popup tester", extensionB->name().c_str()); 256 EXPECT_STREQ("A page action which removes a popup.", 257 extensionC->name().c_str()); 258 259 // Unload B, then C, then A. 260 UnloadExtension(idB); 261 UnloadExtension(idC); 262 UnloadExtension(idA); 263 264 // Load C, then A, then B. 265 ASSERT_TRUE(LoadExtension(extension_c_path)); 266 ASSERT_TRUE(LoadExtension(extension_a_path)); 267 ASSERT_TRUE(LoadExtension(extension_b_path)); 268 EXPECT_EQ(0, moved_count_); 269 270 extensionA = ExtensionAt(0); 271 extensionB = ExtensionAt(1); 272 extensionC = ExtensionAt(2); 273 274 // Make sure we get the order we started with (A, B, C). 275 EXPECT_STREQ(idA.c_str(), extensionA->id().c_str()); 276 EXPECT_STREQ(idB.c_str(), extensionB->id().c_str()); 277 EXPECT_STREQ(idC.c_str(), extensionC->id().c_str()); 278 279 // Put C in the middle and A to the end. 280 model_->MoveBrowserAction(extensionC, 1); 281 model_->MoveBrowserAction(extensionA, 2); 282 283 // Make sure we get this order (C, B, A). 284 EXPECT_STREQ(idC.c_str(), ExtensionAt(0)->id().c_str()); 285 EXPECT_STREQ(idB.c_str(), ExtensionAt(1)->id().c_str()); 286 EXPECT_STREQ(idA.c_str(), ExtensionAt(2)->id().c_str()); 287 288 // Disable B, then C, then A. 289 DisableExtension(idB); 290 DisableExtension(idC); 291 DisableExtension(idA); 292 293 // Enable C, then A, then B. 294 EnableExtension(idA); 295 EnableExtension(idB); 296 EnableExtension(idC); 297 298 // Make sure we get the order we started with. 299 EXPECT_STREQ(idC.c_str(), ExtensionAt(0)->id().c_str()); 300 EXPECT_STREQ(idB.c_str(), ExtensionAt(1)->id().c_str()); 301 EXPECT_STREQ(idA.c_str(), ExtensionAt(2)->id().c_str()); 302 } 303 304 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Uninstall) { 305 // Load two extensions with browser action. 306 base::FilePath extension_a_path(test_data_dir_.AppendASCII("api_test") 307 .AppendASCII("browser_action") 308 .AppendASCII("basics")); 309 ASSERT_TRUE(LoadExtension(extension_a_path)); 310 base::FilePath extension_b_path(test_data_dir_.AppendASCII("api_test") 311 .AppendASCII("browser_action") 312 .AppendASCII("popup")); 313 ASSERT_TRUE(LoadExtension(extension_b_path)); 314 315 // Verify we got what we came for. 316 const Extension* extensionA = ExtensionAt(0); 317 const Extension* extensionB = ExtensionAt(1); 318 std::string idA = extensionA->id(); 319 std::string idB = extensionB->id(); 320 EXPECT_STREQ("A browser action with no icon that makes the page red", 321 extensionA->name().c_str()); 322 EXPECT_STREQ("Popup tester", extensionB->name().c_str()); 323 324 // Order is now A, B. Make B first. 325 model_->MoveBrowserAction(extensionB, 0); 326 327 // Order is now B, A. Uninstall Extension B. 328 UninstallExtension(idB); 329 330 // List contains only A now. Validate that. 331 EXPECT_STREQ(idA.c_str(), ExtensionAt(0)->id().c_str()); 332 EXPECT_EQ(1u, model_->toolbar_items().size()); 333 334 // Load Extension B again. 335 ASSERT_TRUE(LoadExtension(extension_b_path)); 336 EXPECT_EQ(2u, model_->toolbar_items().size()); 337 338 // Make sure Extension B is _not_ first (should have been forgotten at 339 // uninstall time). 340 EXPECT_STREQ(idA.c_str(), ExtensionAt(0)->id().c_str()); 341 EXPECT_STREQ(idB.c_str(), ExtensionAt(1)->id().c_str()); 342 } 343 344 IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderOnPrefChange) { 345 // Load three extensions with browser action. 346 base::FilePath extension_a_path(test_data_dir_.AppendASCII("api_test") 347 .AppendASCII("browser_action") 348 .AppendASCII("basics")); 349 ASSERT_TRUE(LoadExtension(extension_a_path)); 350 base::FilePath extension_b_path(test_data_dir_.AppendASCII("api_test") 351 .AppendASCII("browser_action") 352 .AppendASCII("popup")); 353 ASSERT_TRUE(LoadExtension(extension_b_path)); 354 base::FilePath extension_c_path(test_data_dir_.AppendASCII("api_test") 355 .AppendASCII("browser_action") 356 .AppendASCII("remove_popup")); 357 ASSERT_TRUE(LoadExtension(extension_c_path)); 358 std::string id_a = ExtensionAt(0)->id(); 359 std::string id_b = ExtensionAt(1)->id(); 360 std::string id_c = ExtensionAt(2)->id(); 361 362 // Change value of toolbar preference. 363 extensions::ExtensionIdList new_order; 364 new_order.push_back(id_c); 365 new_order.push_back(id_b); 366 extensions::ExtensionPrefs::Get(browser()->profile())->SetToolbarOrder( 367 new_order); 368 369 // Verify order is changed. 370 EXPECT_EQ(id_c, ExtensionAt(0)->id()); 371 EXPECT_EQ(id_b, ExtensionAt(1)->id()); 372 EXPECT_EQ(id_a, ExtensionAt(2)->id()); 373 } 374