• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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