1 // Copyright 2014 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 "base/macros.h"
6 #include "base/memory/ref_counted.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_service_test_base.h"
11 #include "chrome/browser/extensions/extension_toolbar_model.h"
12 #include "chrome/browser/extensions/test_extension_system.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/extensions/api/extension_action/action_info.h"
15 #include "components/crx_file/id_util.h"
16 #include "extensions/browser/extension_prefs.h"
17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_builder.h"
21 #include "extensions/common/feature_switch.h"
22 #include "extensions/common/manifest_constants.h"
23 #include "extensions/common/value_builder.h"
24
25 namespace extensions {
26
27 namespace {
28
29 // Create an extension. If |action_key| is non-NULL, it should point to either
30 // kBrowserAction or kPageAction, and the extension will have the associated
31 // action.
GetActionExtension(const std::string & name,const char * action_key)32 scoped_refptr<const Extension> GetActionExtension(const std::string& name,
33 const char* action_key) {
34 DictionaryBuilder manifest;
35 manifest.Set("name", name)
36 .Set("description", "An extension")
37 .Set("manifest_version", 2)
38 .Set("version", "1.0.0");
39 if (action_key)
40 manifest.Set(action_key, DictionaryBuilder().Pass());
41
42 return ExtensionBuilder().SetManifest(manifest.Pass())
43 .SetID(crx_file::id_util::GenerateId(name))
44 .Build();
45 }
46
47 // A simple observer that tracks the number of times certain events occur.
48 class ExtensionToolbarModelTestObserver
49 : public ExtensionToolbarModel::Observer {
50 public:
51 explicit ExtensionToolbarModelTestObserver(ExtensionToolbarModel* model);
52 virtual ~ExtensionToolbarModelTestObserver();
53
inserted_count() const54 size_t inserted_count() const { return inserted_count_; }
removed_count() const55 size_t removed_count() const { return removed_count_; }
moved_count() const56 size_t moved_count() const { return moved_count_; }
highlight_mode_count() const57 int highlight_mode_count() const { return highlight_mode_count_; }
58
59 private:
60 // ExtensionToolbarModel::Observer:
ToolbarExtensionAdded(const Extension * extension,int index)61 virtual void ToolbarExtensionAdded(const Extension* extension,
62 int index) OVERRIDE {
63 ++inserted_count_;
64 }
65
ToolbarExtensionRemoved(const Extension * extension)66 virtual void ToolbarExtensionRemoved(const Extension* extension) OVERRIDE {
67 ++removed_count_;
68 }
69
ToolbarExtensionMoved(const Extension * extension,int index)70 virtual void ToolbarExtensionMoved(const Extension* extension,
71 int index) OVERRIDE {
72 ++moved_count_;
73 }
74
ToolbarExtensionUpdated(const Extension * extension)75 virtual void ToolbarExtensionUpdated(const Extension* extension) OVERRIDE {
76 }
77
ShowExtensionActionPopup(const Extension * extension,bool grant_active_tab)78 virtual bool ShowExtensionActionPopup(const Extension* extension,
79 bool grant_active_tab) OVERRIDE {
80 return false;
81 }
82
ToolbarVisibleCountChanged()83 virtual void ToolbarVisibleCountChanged() OVERRIDE {
84 }
85
ToolbarHighlightModeChanged(bool is_highlighting)86 virtual void ToolbarHighlightModeChanged(bool is_highlighting) OVERRIDE {
87 // Add one if highlighting, subtract one if not.
88 highlight_mode_count_ += is_highlighting ? 1 : -1;
89 }
90
GetBrowser()91 virtual Browser* GetBrowser() OVERRIDE {
92 return NULL;
93 }
94
95 ExtensionToolbarModel* model_;
96
97 size_t inserted_count_;
98 size_t removed_count_;
99 size_t moved_count_;
100 // Int because it could become negative (if something goes wrong).
101 int highlight_mode_count_;
102 };
103
ExtensionToolbarModelTestObserver(ExtensionToolbarModel * model)104 ExtensionToolbarModelTestObserver::ExtensionToolbarModelTestObserver(
105 ExtensionToolbarModel* model) : model_(model),
106 inserted_count_(0u),
107 removed_count_(0u),
108 moved_count_(0u),
109 highlight_mode_count_(0) {
110 model_->AddObserver(this);
111 }
112
~ExtensionToolbarModelTestObserver()113 ExtensionToolbarModelTestObserver::~ExtensionToolbarModelTestObserver() {
114 model_->RemoveObserver(this);
115 }
116
117 } // namespace
118
119 class ExtensionToolbarModelUnitTest : public ExtensionServiceTestBase {
120 protected:
121 // Initialize the ExtensionService, ExtensionToolbarModel, and
122 // ExtensionSystem.
123 void Init();
124
125 // Adds or removes the given |extension| and verify success.
126 testing::AssertionResult AddExtension(
127 const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
128 testing::AssertionResult RemoveExtension(
129 const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
130
131 // Adds three extensions, all with browser actions.
132 testing::AssertionResult AddBrowserActionExtensions() WARN_UNUSED_RESULT;
133
134 // Adds three extensions, one each for browser action, page action, and no
135 // action, and are added in that order.
136 testing::AssertionResult AddActionExtensions() WARN_UNUSED_RESULT;
137
138 // Returns the extension at the given index in the toolbar model, or NULL
139 // if one does not exist.
140 const Extension* GetExtensionAtIndex(size_t index) const;
141
toolbar_model()142 ExtensionToolbarModel* toolbar_model() { return toolbar_model_.get(); }
143
observer() const144 const ExtensionToolbarModelTestObserver* observer() const {
145 return model_observer_.get();
146 }
num_toolbar_items() const147 size_t num_toolbar_items() const {
148 return toolbar_model_->toolbar_items().size();
149 }
browser_action_a() const150 const Extension* browser_action_a() const { return browser_action_a_.get(); }
browser_action_b() const151 const Extension* browser_action_b() const { return browser_action_b_.get(); }
browser_action_c() const152 const Extension* browser_action_c() const { return browser_action_c_.get(); }
browser_action() const153 const Extension* browser_action() const {
154 return browser_action_extension_.get();
155 }
page_action() const156 const Extension* page_action() const { return page_action_extension_.get(); }
no_action() const157 const Extension* no_action() const { return no_action_extension_.get(); }
158
159 private:
160 // Verifies that all extensions in |extensions| are added successfully.
161 testing::AssertionResult AddAndVerifyExtensions(
162 const ExtensionList& extensions);
163
164 // The associated (and owned) toolbar model.
165 scoped_ptr<ExtensionToolbarModel> toolbar_model_;
166
167 // The test observer to track events. Must come after toolbar_model_ so that
168 // it is destroyed and removes itself as an observer first.
169 scoped_ptr<ExtensionToolbarModelTestObserver> model_observer_;
170
171 // Sample extensions with only browser actions.
172 scoped_refptr<const Extension> browser_action_a_;
173 scoped_refptr<const Extension> browser_action_b_;
174 scoped_refptr<const Extension> browser_action_c_;
175
176 // Sample extensions with different kinds of actions.
177 scoped_refptr<const Extension> browser_action_extension_;
178 scoped_refptr<const Extension> page_action_extension_;
179 scoped_refptr<const Extension> no_action_extension_;
180 };
181
Init()182 void ExtensionToolbarModelUnitTest::Init() {
183 InitializeEmptyExtensionService();
184 toolbar_model_.reset(
185 new ExtensionToolbarModel(profile(), ExtensionPrefs::Get(profile())));
186 model_observer_.reset(
187 new ExtensionToolbarModelTestObserver(toolbar_model_.get()));
188 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))->
189 SetReady();
190 // Run tasks posted to TestExtensionSystem.
191 base::RunLoop().RunUntilIdle();
192 }
193
AddExtension(const scoped_refptr<const Extension> & extension)194 testing::AssertionResult ExtensionToolbarModelUnitTest::AddExtension(
195 const scoped_refptr<const Extension>& extension) {
196 if (registry()->enabled_extensions().GetByID(extension->id())) {
197 return testing::AssertionFailure() << "Extension " << extension->name() <<
198 " already installed!";
199 }
200 service()->AddExtension(extension.get());
201 if (!registry()->enabled_extensions().GetByID(extension->id())) {
202 return testing::AssertionFailure() << "Failed to install extension: " <<
203 extension->name();
204 }
205 return testing::AssertionSuccess();
206 }
207
RemoveExtension(const scoped_refptr<const Extension> & extension)208 testing::AssertionResult ExtensionToolbarModelUnitTest::RemoveExtension(
209 const scoped_refptr<const Extension>& extension) {
210 if (!registry()->enabled_extensions().GetByID(extension->id())) {
211 return testing::AssertionFailure() << "Extension " << extension->name() <<
212 " not installed!";
213 }
214 service()->UnloadExtension(extension->id(),
215 UnloadedExtensionInfo::REASON_DISABLE);
216 if (registry()->enabled_extensions().GetByID(extension->id())) {
217 return testing::AssertionFailure() << "Failed to unload extension: " <<
218 extension->name();
219 }
220 return testing::AssertionSuccess();
221 }
222
AddActionExtensions()223 testing::AssertionResult ExtensionToolbarModelUnitTest::AddActionExtensions() {
224 browser_action_extension_ =
225 GetActionExtension("browser_action", manifest_keys::kBrowserAction);
226 page_action_extension_ =
227 GetActionExtension("page_action", manifest_keys::kPageAction);
228 no_action_extension_ = GetActionExtension("no_action", NULL);
229
230 ExtensionList extensions;
231 extensions.push_back(browser_action_extension_);
232 extensions.push_back(page_action_extension_);
233 extensions.push_back(no_action_extension_);
234
235 return AddAndVerifyExtensions(extensions);
236 }
237
238 testing::AssertionResult
AddBrowserActionExtensions()239 ExtensionToolbarModelUnitTest::AddBrowserActionExtensions() {
240 browser_action_a_ =
241 GetActionExtension("browser_actionA", manifest_keys::kBrowserAction);
242 browser_action_b_ =
243 GetActionExtension("browser_actionB", manifest_keys::kBrowserAction);
244 browser_action_c_ =
245 GetActionExtension("browser_actionC", manifest_keys::kBrowserAction);
246
247 ExtensionList extensions;
248 extensions.push_back(browser_action_a_);
249 extensions.push_back(browser_action_b_);
250 extensions.push_back(browser_action_c_);
251
252 return AddAndVerifyExtensions(extensions);
253 }
254
GetExtensionAtIndex(size_t index) const255 const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
256 size_t index) const {
257 return index < toolbar_model_->toolbar_items().size()
258 ? toolbar_model_->toolbar_items()[index].get()
259 : NULL;
260 }
261
AddAndVerifyExtensions(const ExtensionList & extensions)262 testing::AssertionResult ExtensionToolbarModelUnitTest::AddAndVerifyExtensions(
263 const ExtensionList& extensions) {
264 for (ExtensionList::const_iterator iter = extensions.begin();
265 iter != extensions.end(); ++iter) {
266 if (!AddExtension(*iter)) {
267 return testing::AssertionFailure() << "Failed to install extension: " <<
268 (*iter)->name();
269 }
270 }
271 return testing::AssertionSuccess();
272 }
273
274 // A basic test for extensions with browser actions showing up in the toolbar.
TEST_F(ExtensionToolbarModelUnitTest,BasicExtensionToolbarModelTest)275 TEST_F(ExtensionToolbarModelUnitTest, BasicExtensionToolbarModelTest) {
276 Init();
277
278 // Load an extension with no browser action.
279 scoped_refptr<const Extension> extension1 =
280 GetActionExtension("no_action", NULL);
281 ASSERT_TRUE(AddExtension(extension1));
282
283 // This extension should not be in the model (has no browser action).
284 EXPECT_EQ(0u, observer()->inserted_count());
285 EXPECT_EQ(0u, num_toolbar_items());
286 EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
287
288 // Load an extension with a browser action.
289 scoped_refptr<const Extension> extension2 =
290 GetActionExtension("browser_action", manifest_keys::kBrowserAction);
291 ASSERT_TRUE(AddExtension(extension2));
292
293 // We should now find our extension in the model.
294 EXPECT_EQ(1u, observer()->inserted_count());
295 EXPECT_EQ(1u, num_toolbar_items());
296 EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
297
298 // Should be a no-op, but still fires the events.
299 toolbar_model()->MoveExtensionIcon(extension2.get(), 0);
300 EXPECT_EQ(1u, observer()->moved_count());
301 EXPECT_EQ(1u, num_toolbar_items());
302 EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
303
304 // Remove the extension and verify.
305 ASSERT_TRUE(RemoveExtension(extension2));
306 EXPECT_EQ(1u, observer()->removed_count());
307 EXPECT_EQ(0u, num_toolbar_items());
308 EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
309 }
310
311 // Test various different reorderings, removals, and reinsertions.
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarReorderAndReinsert)312 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarReorderAndReinsert) {
313 Init();
314
315 // Add the three browser action extensions.
316 ASSERT_TRUE(AddBrowserActionExtensions());
317
318 // Verify the three extensions are in the model in the proper order.
319 EXPECT_EQ(3u, num_toolbar_items());
320 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
321 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
322 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
323
324 // Order is now A, B, C. Let's put C first.
325 toolbar_model()->MoveExtensionIcon(browser_action_c(), 0);
326 EXPECT_EQ(1u, observer()->moved_count());
327 EXPECT_EQ(3u, num_toolbar_items());
328 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
329 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
330 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
331
332 // Order is now C, A, B. Let's put A last.
333 toolbar_model()->MoveExtensionIcon(browser_action_a(), 2);
334 EXPECT_EQ(2u, observer()->moved_count());
335 EXPECT_EQ(3u, num_toolbar_items());
336 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
337 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
338 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
339
340 // Order is now C, B, A. Let's remove B.
341 ASSERT_TRUE(RemoveExtension(browser_action_b()));
342 EXPECT_EQ(1u, observer()->removed_count());
343 EXPECT_EQ(2u, num_toolbar_items());
344 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
345 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
346
347 // Load extension B again.
348 ASSERT_TRUE(AddExtension(browser_action_b()));
349
350 // Extension B loaded again.
351 EXPECT_EQ(4u, observer()->inserted_count());
352 EXPECT_EQ(3u, num_toolbar_items());
353 // Make sure it gets its old spot in the list.
354 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
355
356 // Unload B again.
357 ASSERT_TRUE(RemoveExtension(browser_action_b()));
358 EXPECT_EQ(2u, observer()->removed_count());
359 EXPECT_EQ(2u, num_toolbar_items());
360
361 // Order is now C, A. Flip it.
362 toolbar_model()->MoveExtensionIcon(browser_action_a(), 0);
363 EXPECT_EQ(3u, observer()->moved_count());
364 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
365 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
366
367 // Move A to the location it already occupies.
368 toolbar_model()->MoveExtensionIcon(browser_action_a(), 0);
369 EXPECT_EQ(4u, observer()->moved_count());
370 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
371 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
372
373 // Order is now A, C. Remove C.
374 ASSERT_TRUE(RemoveExtension(browser_action_c()));
375 EXPECT_EQ(3u, observer()->removed_count());
376 EXPECT_EQ(1u, num_toolbar_items());
377 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
378
379 // Load extension C again.
380 ASSERT_TRUE(AddExtension(browser_action_c()));
381
382 // Extension C loaded again.
383 EXPECT_EQ(5u, observer()->inserted_count());
384 EXPECT_EQ(2u, num_toolbar_items());
385 // Make sure it gets its old spot in the list (at the very end).
386 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
387 }
388
389 // Test that order persists after unloading and disabling, but not across
390 // uninstallation.
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarUnloadDisableAndUninstall)391 TEST_F(ExtensionToolbarModelUnitTest,
392 ExtensionToolbarUnloadDisableAndUninstall) {
393 Init();
394
395 // Add the three browser action extensions.
396 ASSERT_TRUE(AddBrowserActionExtensions());
397
398 // Verify the three extensions are in the model in the proper order: A, B, C.
399 EXPECT_EQ(3u, num_toolbar_items());
400 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
401 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
402 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
403
404 // Unload B, then C, then A, and then reload C, then A, then B.
405 ASSERT_TRUE(RemoveExtension(browser_action_b()));
406 ASSERT_TRUE(RemoveExtension(browser_action_c()));
407 ASSERT_TRUE(RemoveExtension(browser_action_a()));
408 EXPECT_EQ(0u, num_toolbar_items()); // Sanity check: all gone?
409 ASSERT_TRUE(AddExtension(browser_action_c()));
410 ASSERT_TRUE(AddExtension(browser_action_a()));
411 ASSERT_TRUE(AddExtension(browser_action_b()));
412 EXPECT_EQ(3u, num_toolbar_items()); // Sanity check: all back?
413 EXPECT_EQ(0u, observer()->moved_count());
414
415 // Even though we unloaded and reloaded in a different order, the original
416 // order (A, B, C) should be preserved.
417 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
418 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
419 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
420
421 // Disabling extensions should also preserve order.
422 service()->DisableExtension(browser_action_b()->id(),
423 Extension::DISABLE_USER_ACTION);
424 service()->DisableExtension(browser_action_c()->id(),
425 Extension::DISABLE_USER_ACTION);
426 service()->DisableExtension(browser_action_a()->id(),
427 Extension::DISABLE_USER_ACTION);
428 service()->EnableExtension(browser_action_c()->id());
429 service()->EnableExtension(browser_action_a()->id());
430 service()->EnableExtension(browser_action_b()->id());
431
432 // Make sure we still get the original A, B, C order.
433 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
434 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
435 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
436
437 // Move browser_action_b() to be first.
438 toolbar_model()->MoveExtensionIcon(browser_action_b(), 0);
439 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
440
441 // Uninstall Extension B.
442 service()->UninstallExtension(browser_action_b()->id(),
443 UNINSTALL_REASON_FOR_TESTING,
444 base::Bind(&base::DoNothing),
445 NULL); // Ignore error.
446 // List contains only A and C now. Validate that.
447 EXPECT_EQ(2u, num_toolbar_items());
448 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
449 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
450
451 ASSERT_TRUE(AddExtension(browser_action_b()));
452
453 // Make sure Extension B is _not_ first (its old position should have been
454 // forgotten at uninstall time). Order should be A, C, B.
455 EXPECT_EQ(3u, num_toolbar_items());
456 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
457 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
458 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
459 }
460
TEST_F(ExtensionToolbarModelUnitTest,ReorderOnPrefChange)461 TEST_F(ExtensionToolbarModelUnitTest, ReorderOnPrefChange) {
462 Init();
463
464 // Add the three browser action extensions.
465 ASSERT_TRUE(AddBrowserActionExtensions());
466 EXPECT_EQ(3u, num_toolbar_items());
467
468 // Change the value of the toolbar preference.
469 ExtensionIdList new_order;
470 new_order.push_back(browser_action_c()->id());
471 new_order.push_back(browser_action_b()->id());
472 ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
473
474 // Verify order is changed.
475 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
476 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
477 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
478 }
479
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarHighlightMode)480 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightMode) {
481 Init();
482
483 EXPECT_FALSE(toolbar_model()->HighlightExtensions(ExtensionIdList()));
484 EXPECT_EQ(0, observer()->highlight_mode_count());
485
486 // Add the three browser action extensions.
487 ASSERT_TRUE(AddBrowserActionExtensions());
488 EXPECT_EQ(3u, num_toolbar_items());
489
490 // Highlight one extension.
491 ExtensionIdList extension_ids;
492 extension_ids.push_back(browser_action_b()->id());
493 toolbar_model()->HighlightExtensions(extension_ids);
494 EXPECT_EQ(1, observer()->highlight_mode_count());
495 EXPECT_TRUE(toolbar_model()->is_highlighting());
496
497 EXPECT_EQ(1u, num_toolbar_items());
498 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
499
500 // Stop highlighting.
501 toolbar_model()->StopHighlighting();
502 EXPECT_EQ(0, observer()->highlight_mode_count());
503 EXPECT_FALSE(toolbar_model()->is_highlighting());
504
505 // Verify that the extensions are back to normal.
506 EXPECT_EQ(3u, num_toolbar_items());
507 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
508 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
509 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
510
511 // Call stop highlighting a second time (shouldn't be notified).
512 toolbar_model()->StopHighlighting();
513 EXPECT_EQ(0, observer()->highlight_mode_count());
514 EXPECT_FALSE(toolbar_model()->is_highlighting());
515
516 // Highlight all extensions.
517 extension_ids.clear();
518 extension_ids.push_back(browser_action_a()->id());
519 extension_ids.push_back(browser_action_b()->id());
520 extension_ids.push_back(browser_action_c()->id());
521 toolbar_model()->HighlightExtensions(extension_ids);
522 EXPECT_EQ(1, observer()->highlight_mode_count());
523 EXPECT_EQ(3u, num_toolbar_items());
524 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
525 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
526 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
527
528 // Highlight only extension b (shrink the highlight list).
529 extension_ids.clear();
530 extension_ids.push_back(browser_action_b()->id());
531 toolbar_model()->HighlightExtensions(extension_ids);
532 EXPECT_EQ(2, observer()->highlight_mode_count());
533 EXPECT_EQ(1u, num_toolbar_items());
534 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
535
536 // Highlight extensions a and b (grow the highlight list).
537 extension_ids.clear();
538 extension_ids.push_back(browser_action_a()->id());
539 extension_ids.push_back(browser_action_b()->id());
540 toolbar_model()->HighlightExtensions(extension_ids);
541 EXPECT_EQ(3, observer()->highlight_mode_count());
542 EXPECT_EQ(2u, num_toolbar_items());
543 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
544 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
545
546 // Highlight no extensions (empty the highlight list).
547 extension_ids.clear();
548 toolbar_model()->HighlightExtensions(extension_ids);
549 EXPECT_EQ(2, observer()->highlight_mode_count());
550 EXPECT_FALSE(toolbar_model()->is_highlighting());
551 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
552 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
553 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
554 }
555
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarHighlightModeRemove)556 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeRemove) {
557 Init();
558
559 // Add the three browser action extensions.
560 ASSERT_TRUE(AddBrowserActionExtensions());
561 EXPECT_EQ(3u, num_toolbar_items());
562
563 // Highlight two of the extensions.
564 ExtensionIdList extension_ids;
565 extension_ids.push_back(browser_action_a()->id());
566 extension_ids.push_back(browser_action_b()->id());
567 toolbar_model()->HighlightExtensions(extension_ids);
568 EXPECT_TRUE(toolbar_model()->is_highlighting());
569 EXPECT_EQ(1, observer()->highlight_mode_count());
570 EXPECT_EQ(2u, num_toolbar_items());
571
572 // Disable one of them - only one should remain highlighted.
573 service()->DisableExtension(browser_action_a()->id(),
574 Extension::DISABLE_USER_ACTION);
575 EXPECT_TRUE(toolbar_model()->is_highlighting());
576 EXPECT_EQ(1u, num_toolbar_items());
577 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
578
579 // Uninstall the remaining highlighted extension. This should result in
580 // highlight mode exiting.
581 service()->UninstallExtension(browser_action_b()->id(),
582 UNINSTALL_REASON_FOR_TESTING,
583 base::Bind(&base::DoNothing),
584 NULL); // Ignore error.
585 EXPECT_FALSE(toolbar_model()->is_highlighting());
586 EXPECT_EQ(0, observer()->highlight_mode_count());
587 EXPECT_EQ(1u, num_toolbar_items());
588 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
589
590 // Test that removing an unhighlighted extension still works.
591 // Reinstall extension b, and then highlight extension c.
592 ASSERT_TRUE(AddExtension(browser_action_b()));
593 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
594 extension_ids.clear();
595 extension_ids.push_back(browser_action_c()->id());
596 toolbar_model()->HighlightExtensions(extension_ids);
597 EXPECT_EQ(1, observer()->highlight_mode_count());
598 EXPECT_TRUE(toolbar_model()->is_highlighting());
599 EXPECT_EQ(1u, num_toolbar_items());
600 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
601
602 // Uninstalling b should not have visible impact.
603 service()->UninstallExtension(browser_action_b()->id(),
604 UNINSTALL_REASON_FOR_TESTING,
605 base::Bind(&base::DoNothing),
606 NULL); // Ignore error.
607 EXPECT_TRUE(toolbar_model()->is_highlighting());
608 EXPECT_EQ(1, observer()->highlight_mode_count());
609 EXPECT_EQ(1u, num_toolbar_items());
610 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
611
612 // When we stop, only extension c should remain.
613 toolbar_model()->StopHighlighting();
614 EXPECT_FALSE(toolbar_model()->is_highlighting());
615 EXPECT_EQ(0, observer()->highlight_mode_count());
616 EXPECT_EQ(1u, num_toolbar_items());
617 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
618 }
619
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarHighlightModeAdd)620 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeAdd) {
621 Init();
622
623 // Add the three browser action extensions.
624 ASSERT_TRUE(AddBrowserActionExtensions());
625 EXPECT_EQ(3u, num_toolbar_items());
626
627 // Remove one (down to two).
628 ASSERT_TRUE(RemoveExtension(browser_action_c()));
629
630 // Highlight one of the two extensions.
631 ExtensionIdList extension_ids;
632 extension_ids.push_back(browser_action_a()->id());
633 toolbar_model()->HighlightExtensions(extension_ids);
634 EXPECT_TRUE(toolbar_model()->is_highlighting());
635 EXPECT_EQ(1u, num_toolbar_items());
636 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
637
638 // Adding a new extension should have no visible effect.
639 ASSERT_TRUE(AddExtension(browser_action_c()));
640 EXPECT_TRUE(toolbar_model()->is_highlighting());
641 EXPECT_EQ(1u, num_toolbar_items());
642 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
643
644 // When we stop highlighting, we should see the new extension show up.
645 toolbar_model()->StopHighlighting();
646 EXPECT_FALSE(toolbar_model()->is_highlighting());
647 EXPECT_EQ(3u, num_toolbar_items());
648 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
649 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
650 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
651 }
652
653 // Test that the extension toolbar maintains the proper size, even after a pref
654 // change.
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarSizeAfterPrefChange)655 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarSizeAfterPrefChange) {
656 Init();
657
658 // Add the three browser action extensions.
659 ASSERT_TRUE(AddBrowserActionExtensions());
660 EXPECT_EQ(3u, num_toolbar_items());
661
662 // Should be at max size (-1).
663 EXPECT_EQ(-1, toolbar_model()->GetVisibleIconCount());
664 toolbar_model()->OnExtensionToolbarPrefChange();
665 // Should still be at max size.
666 EXPECT_EQ(-1, toolbar_model()->GetVisibleIconCount());
667 }
668
669 // Test that, in the absence of the extension-action-redesign switch, the
670 // model only contains extensions with browser actions.
TEST_F(ExtensionToolbarModelUnitTest,TestToolbarExtensionTypesNoSwitch)671 TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesNoSwitch) {
672 Init();
673 ASSERT_TRUE(AddActionExtensions());
674
675 EXPECT_EQ(1u, num_toolbar_items());
676 EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
677 }
678
679 // Test that, with the extension-action-redesign switch, the model contains
680 // all types of extensions, except those which should not be displayed on the
681 // toolbar (like component extensions).
TEST_F(ExtensionToolbarModelUnitTest,TestToolbarExtensionTypesSwitch)682 TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesSwitch) {
683 FeatureSwitch::ScopedOverride enable_redesign(
684 FeatureSwitch::extension_action_redesign(), true);
685 Init();
686 ASSERT_TRUE(AddActionExtensions());
687
688 // With the switch on, extensions with page actions and no action should also
689 // be displayed in the toolbar.
690 EXPECT_EQ(3u, num_toolbar_items());
691 EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
692 EXPECT_EQ(page_action(), GetExtensionAtIndex(1u));
693 EXPECT_EQ(no_action(), GetExtensionAtIndex(2u));
694 }
695
696 // Test that hiding actions on the toolbar results in their removal from the
697 // model when the redesign switch is not enabled.
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarActionsVisibilityNoSwitch)698 TEST_F(ExtensionToolbarModelUnitTest,
699 ExtensionToolbarActionsVisibilityNoSwitch) {
700 Init();
701
702 ASSERT_TRUE(AddBrowserActionExtensions());
703 // Sanity check: Order should start as A B C.
704 EXPECT_EQ(3u, num_toolbar_items());
705 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
706 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
707 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
708
709 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
710
711 // By default, all actions should be visible.
712 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
713 prefs, browser_action_a()->id()));
714 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
715 prefs, browser_action_b()->id()));
716 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
717 prefs, browser_action_c()->id()));
718
719 // Hiding an action should result in its removal from the toolbar.
720 ExtensionActionAPI::SetBrowserActionVisibility(
721 prefs, browser_action_b()->id(), false);
722 EXPECT_FALSE(ExtensionActionAPI::GetBrowserActionVisibility(
723 prefs, browser_action_b()->id()));
724 // Thus, there should now only be two items on the toolbar - A and C.
725 EXPECT_EQ(2u, num_toolbar_items());
726 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
727 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
728
729 // Resetting the visibility to 'true' should result in the extension being
730 // added back at its original position.
731 ExtensionActionAPI::SetBrowserActionVisibility(
732 prefs, browser_action_b()->id(), true);
733 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
734 prefs, browser_action_b()->id()));
735 // So the toolbar order should be A B C.
736 EXPECT_EQ(3u, num_toolbar_items());
737 EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
738 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
739 EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
740 }
741
742 // Test that hiding actions on the toolbar results in sending them to the
743 // overflow menu when the redesign switch is enabled.
TEST_F(ExtensionToolbarModelUnitTest,ExtensionToolbarActionsVisibilityWithSwitch)744 TEST_F(ExtensionToolbarModelUnitTest,
745 ExtensionToolbarActionsVisibilityWithSwitch) {
746 FeatureSwitch::ScopedOverride enable_redesign(
747 FeatureSwitch::extension_action_redesign(), true);
748 Init();
749
750 // We choose to use all types of extensions here, since the misnamed
751 // BrowserActionVisibility is now for toolbar visibility.
752 ASSERT_TRUE(AddActionExtensions());
753
754 // For readability, alias extensions A B C.
755 const Extension* extension_a = browser_action();
756 const Extension* extension_b = page_action();
757 const Extension* extension_c = no_action();
758
759 // Sanity check: Order should start as A B C, with all three visible.
760 EXPECT_EQ(3u, num_toolbar_items());
761 EXPECT_EQ(-1, toolbar_model()->GetVisibleIconCount()); // -1 = 'all'.
762 EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
763 EXPECT_EQ(extension_b, GetExtensionAtIndex(1u));
764 EXPECT_EQ(extension_c, GetExtensionAtIndex(2u));
765
766 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
767
768 // By default, all actions should be visible.
769 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
770 prefs, extension_a->id()));
771 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
772 prefs, extension_c->id()));
773 EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
774 prefs, extension_b->id()));
775
776 // Hiding an action should result in it being sent to the overflow menu.
777 ExtensionActionAPI::SetBrowserActionVisibility(
778 prefs, extension_b->id(), false);
779
780 // Thus, the order should be A C B, with B in the overflow.
781 EXPECT_EQ(3u, num_toolbar_items());
782 EXPECT_EQ(2, toolbar_model()->GetVisibleIconCount());
783 EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
784 EXPECT_EQ(extension_c, GetExtensionAtIndex(1u));
785 EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
786
787 // Hiding an extension's action should result in it being sent to the overflow
788 // as well, but as the _first_ extension in the overflow.
789 ExtensionActionAPI::SetBrowserActionVisibility(
790 prefs, extension_a->id(), false);
791 // Thus, the order should be C A B, with A and B in the overflow.
792 EXPECT_EQ(3u, num_toolbar_items());
793 EXPECT_EQ(1, toolbar_model()->GetVisibleIconCount());
794 EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
795 EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
796 EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
797
798 // Resetting A's visibility to true should send it back to the visible icons
799 // (and should grow visible icons by 1), but it should be added to the end of
800 // the visible icon list (not to its original position).
801 ExtensionActionAPI::SetBrowserActionVisibility(
802 prefs, extension_a->id(), true);
803 // So order is C A B, with only B in the overflow.
804 EXPECT_EQ(3u, num_toolbar_items());
805 EXPECT_EQ(2, toolbar_model()->GetVisibleIconCount());
806 EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
807 EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
808 EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
809
810 // Resetting B to be visible should make the order C A B, with no overflow.
811 ExtensionActionAPI::SetBrowserActionVisibility(
812 prefs, extension_b->id(), true);
813 EXPECT_EQ(3u, num_toolbar_items());
814 EXPECT_EQ(-1, toolbar_model()->GetVisibleIconCount()); // -1 = 'all'
815 EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
816 EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
817 EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
818 }
819
820 } // namespace extensions
821