Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_toolbar_model_unittest.cc
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/files/file_util.h"
6 #include "base/macros.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
11 #include "chrome/browser/extensions/extension_action_manager.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_service_test_base.h"
14 #include "chrome/browser/extensions/extension_toolbar_model.h"
15 #include "chrome/browser/extensions/extension_toolbar_model_factory.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/extensions/test_extension_dir.h"
18 #include "chrome/browser/extensions/test_extension_system.h"
19 #include "chrome/browser/extensions/unpacked_installer.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/session_tab_helper.h"
22 #include "chrome/common/extensions/api/extension_action/action_info.h"
23 #include "components/crx_file/id_util.h"
24 #include "content/public/test/test_renderer_host.h"
25 #include "content/public/test/web_contents_tester.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/browser/test_extension_registry_observer.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/extension_builder.h"
32 #include "extensions/common/feature_switch.h"
33 #include "extensions/common/manifest_constants.h"
34 #include "extensions/common/value_builder.h"
35
36 #if defined(USE_AURA)
37 #include "ui/aura/env.h"
38 #endif
39
40 namespace extensions {
41
42 namespace {
43
44 // A helper class to create test web contents (tabs) for unit tests, without
45 // inheriting from RenderViewTestHarness. Can create web contents, and will
46 // clean up after itself upon destruction. Owns all created web contents.
47 // A few notes:
48 // - Works well allocated on the stack, because it should be destroyed before
49 //   associated browser context.
50 // - Doesn't play nice with web contents created any other way (because of
51 //   the implementation of RenderViewHostTestEnabler). But if you are creating
52 //   web contents already, what do you need this for? ;)
53 // - This will call aura::Env::Create/DeleteInstance(), because that's needed
54 //   for web contents. Nothing else should call it first. (This could be
55 //   tweaked by passing in a flag, if there's need.)
56 // TODO(devlin): Look around and see if this class is needed elsewhere; if so,
57 // move it there and expand the API a bit (methods to, e.g., delete/close a
58 // web contents, access existing web contents, etc).
59 class TestWebContentsFactory {
60  public:
61   TestWebContentsFactory();
62   ~TestWebContentsFactory();
63
64   // Creates a new WebContents with the given |context|, and returns it.
65   content::WebContents* CreateWebContents(content::BrowserContext* context);
66
67  private:
68   // The test factory (and friends) for creating test web contents.
69   scoped_ptr<content::RenderViewHostTestEnabler> rvh_enabler_;
70
71   // The vector of web contents that this class created.
72   ScopedVector<content::WebContents> web_contents_;
73
74   DISALLOW_COPY_AND_ASSIGN(TestWebContentsFactory);
75 };
76
77 TestWebContentsFactory::TestWebContentsFactory()
78     : rvh_enabler_(new content::RenderViewHostTestEnabler()) {
79 #if defined(USE_AURA)
80   aura::Env::CreateInstance(true);
81 #endif
82 }
83
84 TestWebContentsFactory::~TestWebContentsFactory() {
85   web_contents_.clear();
86   // Let any posted tasks for web contents deletion run.
87   base::RunLoop().RunUntilIdle();
88   rvh_enabler_.reset();
89   // Let any posted tasks for RenderProcess/ViewHost deletion run.
90   base::RunLoop().RunUntilIdle();
91
92 #if defined(USE_AURA)
93   aura::Env::DeleteInstance();
94 #endif
95 }
96
97 content::WebContents* TestWebContentsFactory::CreateWebContents(
98     content::BrowserContext* context) {
99   scoped_ptr<content::WebContents> web_contents(
100       content::WebContentsTester::CreateTestWebContents(context, nullptr));
101   DCHECK(web_contents);
102   web_contents_.push_back(web_contents.release());
103   return web_contents_.back();
104 }
105
106 // Creates a new ExtensionToolbarModel for the given |context|.
107 KeyedService* BuildToolbarModel(content::BrowserContext* context) {
108   return new ExtensionToolbarModel(Profile::FromBrowserContext(context),
109                                    ExtensionPrefs::Get(context));
110 }
111
112 // Given a |profile|, assigns the testing keyed service function to
113 // BuildToolbarModel() and uses it to create and initialize a new
114 // ExtensionToolbarModel.
115 ExtensionToolbarModel* CreateToolbarModelForProfile(Profile* profile) {
116   ExtensionToolbarModel* model = ExtensionToolbarModel::Get(profile);
117   if (model)
118     return model;
119
120   // No existing model means it's a new profile (since we, by default, don't
121   // create the ToolbarModel in testing).
122   ExtensionToolbarModelFactory::GetInstance()->SetTestingFactory(
123       profile, &BuildToolbarModel);
124   model = ExtensionToolbarModel::Get(profile);
125   // Fake the extension system ready signal.
126   // HACK ALERT! In production, the ready task on ExtensionSystem (and most
127   // everything else on it, too) is shared between incognito and normal
128   // profiles, but a TestExtensionSystem doesn't have the concept of "shared".
129   // Because of this, we have to set any new profile's TestExtensionSystem's
130   // ready task, too.
131   static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile))->SetReady();
132   // Run tasks posted to TestExtensionSystem.
133   base::RunLoop().RunUntilIdle();
134
135   return model;
136 }
137
138 // Create an extension. If |action_key| is non-NULL, it should point to either
139 // kBrowserAction or kPageAction, and the extension will have the associated
140 // action.
141 scoped_refptr<const Extension> GetActionExtension(const std::string& name,
142                                                   const char* action_key) {
143   DictionaryBuilder manifest;
144   manifest.Set("name", name)
145           .Set("description", "An extension")
146           .Set("manifest_version", 2)
147           .Set("version", "1.0.0");
148   if (action_key)
149     manifest.Set(action_key, DictionaryBuilder().Pass());
150
151   return ExtensionBuilder().SetManifest(manifest.Pass())
152                            .SetID(crx_file::id_util::GenerateId(name))
153                            .Build();
154 }
155
156 // A simple observer that tracks the number of times certain events occur.
157 class ExtensionToolbarModelTestObserver
158     : public ExtensionToolbarModel::Observer {
159  public:
160   explicit ExtensionToolbarModelTestObserver(ExtensionToolbarModel* model);
161   ~ExtensionToolbarModelTestObserver() override;
162
163   size_t inserted_count() const { return inserted_count_; }
164   size_t removed_count() const { return removed_count_; }
165   size_t moved_count() const { return moved_count_; }
166   int highlight_mode_count() const { return highlight_mode_count_; }
167   size_t reorder_count() const { return reorder_count_; }
168
169  private:
170   // ExtensionToolbarModel::Observer:
171   void ToolbarExtensionAdded(const Extension* extension, int index) override {
172     ++inserted_count_;
173   }
174
175   void ToolbarExtensionRemoved(const Extension* extension) override {
176     ++removed_count_;
177   }
178
179   void ToolbarExtensionMoved(const Extension* extension, int index) override {
180     ++moved_count_;
181   }
182
183   void ToolbarExtensionUpdated(const Extension* extension) override {}
184
185   bool ShowExtensionActionPopup(const Extension* extension,
186                                 bool grant_active_tab) override {
187     return false;
188   }
189
190   void ToolbarVisibleCountChanged() override {}
191
192   void ToolbarHighlightModeChanged(bool is_highlighting) override {
193     // Add one if highlighting, subtract one if not.
194     highlight_mode_count_ += is_highlighting ? 1 : -1;
195   }
196
197   void OnToolbarReorderNecessary(content::WebContents* web_contents) override {
198     ++reorder_count_;
199   }
200
201   Browser* GetBrowser() override { return NULL; }
202
203   ExtensionToolbarModel* model_;
204
205   size_t inserted_count_;
206   size_t removed_count_;
207   size_t moved_count_;
208   // Int because it could become negative (if something goes wrong).
209   int highlight_mode_count_;
210   size_t reorder_count_;
211 };
212
213 ExtensionToolbarModelTestObserver::ExtensionToolbarModelTestObserver(
214     ExtensionToolbarModel* model) : model_(model),
215                                     inserted_count_(0),
216                                     removed_count_(0),
217                                     moved_count_(0),
218                                     highlight_mode_count_(0),
219                                     reorder_count_(0) {
220   model_->AddObserver(this);
221 }
222
223 ExtensionToolbarModelTestObserver::~ExtensionToolbarModelTestObserver() {
224   model_->RemoveObserver(this);
225 }
226
227 }  // namespace
228
229 class ExtensionToolbarModelUnitTest : public ExtensionServiceTestBase {
230  protected:
231   // Initialize the ExtensionService, ExtensionToolbarModel, and
232   // ExtensionSystem.
233   void Init();
234
235   void TearDown() override;
236
237   // Adds or removes the given |extension| and verify success.
238   testing::AssertionResult AddExtension(
239       const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
240   testing::AssertionResult RemoveExtension(
241       const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
242
243   // Adds three extensions, all with browser actions.
244   testing::AssertionResult AddBrowserActionExtensions() WARN_UNUSED_RESULT;
245
246   // Adds three extensions, one each for browser action, page action, and no
247   // action, and are added in that order.
248   testing::AssertionResult AddActionExtensions() WARN_UNUSED_RESULT;
249
250   // Returns the extension at the given index in the toolbar model, or NULL
251   // if one does not exist.
252   // If |model| is specified, it is used. Otherwise, this defaults to
253   // |toolbar_model_|.
254   const Extension* GetExtensionAtIndex(
255       size_t index, const ExtensionToolbarModel* model) const;
256   const Extension* GetExtensionAtIndex(size_t index) const;
257
258   ExtensionToolbarModel* toolbar_model() { return toolbar_model_; }
259
260   const ExtensionToolbarModelTestObserver* observer() const {
261     return model_observer_.get();
262   }
263   size_t num_toolbar_items() const {
264     return toolbar_model_->toolbar_items().size();
265   }
266   const Extension* browser_action_a() const { return browser_action_a_.get(); }
267   const Extension* browser_action_b() const { return browser_action_b_.get(); }
268   const Extension* browser_action_c() const { return browser_action_c_.get(); }
269   const Extension* browser_action() const {
270     return browser_action_extension_.get();
271   }
272   const Extension* page_action() const { return page_action_extension_.get(); }
273   const Extension* no_action() const { return no_action_extension_.get(); }
274
275  private:
276   // Verifies that all extensions in |extensions| are added successfully.
277   testing::AssertionResult AddAndVerifyExtensions(
278       const ExtensionList& extensions);
279
280   // The toolbar model associated with the testing profile.
281   ExtensionToolbarModel* toolbar_model_;
282
283   // The test observer to track events. Must come after toolbar_model_ so that
284   // it is destroyed and removes itself as an observer first.
285   scoped_ptr<ExtensionToolbarModelTestObserver> model_observer_;
286
287   // Sample extensions with only browser actions.
288   scoped_refptr<const Extension> browser_action_a_;
289   scoped_refptr<const Extension> browser_action_b_;
290   scoped_refptr<const Extension> browser_action_c_;
291
292   // Sample extensions with different kinds of actions.
293   scoped_refptr<const Extension> browser_action_extension_;
294   scoped_refptr<const Extension> page_action_extension_;
295   scoped_refptr<const Extension> no_action_extension_;
296 };
297
298 void ExtensionToolbarModelUnitTest::Init() {
299   InitializeEmptyExtensionService();
300   toolbar_model_ = CreateToolbarModelForProfile(profile());
301   model_observer_.reset(new ExtensionToolbarModelTestObserver(toolbar_model_));
302 }
303
304 void ExtensionToolbarModelUnitTest::TearDown() {
305   model_observer_.reset();
306   ExtensionServiceTestBase::TearDown();
307 }
308
309 testing::AssertionResult ExtensionToolbarModelUnitTest::AddExtension(
310     const scoped_refptr<const Extension>& extension) {
311   if (registry()->enabled_extensions().GetByID(extension->id())) {
312     return testing::AssertionFailure() << "Extension " << extension->name() <<
313         " already installed!";
314   }
315   service()->AddExtension(extension.get());
316   if (!registry()->enabled_extensions().GetByID(extension->id())) {
317     return testing::AssertionFailure() << "Failed to install extension: " <<
318         extension->name();
319   }
320   return testing::AssertionSuccess();
321 }
322
323 testing::AssertionResult ExtensionToolbarModelUnitTest::RemoveExtension(
324     const scoped_refptr<const Extension>& extension) {
325   if (!registry()->enabled_extensions().GetByID(extension->id())) {
326     return testing::AssertionFailure() << "Extension " << extension->name() <<
327         " not installed!";
328   }
329   service()->UnloadExtension(extension->id(),
330                              UnloadedExtensionInfo::REASON_DISABLE);
331   if (registry()->enabled_extensions().GetByID(extension->id())) {
332     return testing::AssertionFailure() << "Failed to unload extension: " <<
333         extension->name();
334   }
335   return testing::AssertionSuccess();
336 }
337
338 testing::AssertionResult ExtensionToolbarModelUnitTest::AddActionExtensions() {
339   browser_action_extension_ =
340       GetActionExtension("browser_action", manifest_keys::kBrowserAction);
341   page_action_extension_ =
342        GetActionExtension("page_action", manifest_keys::kPageAction);
343   no_action_extension_ = GetActionExtension("no_action", NULL);
344
345   ExtensionList extensions;
346   extensions.push_back(browser_action_extension_);
347   extensions.push_back(page_action_extension_);
348   extensions.push_back(no_action_extension_);
349
350   return AddAndVerifyExtensions(extensions);
351 }
352
353 testing::AssertionResult
354 ExtensionToolbarModelUnitTest::AddBrowserActionExtensions() {
355   browser_action_a_ =
356       GetActionExtension("browser_actionA", manifest_keys::kBrowserAction);
357   browser_action_b_ =
358       GetActionExtension("browser_actionB", manifest_keys::kBrowserAction);
359   browser_action_c_ =
360       GetActionExtension("browser_actionC", manifest_keys::kBrowserAction);
361
362   ExtensionList extensions;
363   extensions.push_back(browser_action_a_);
364   extensions.push_back(browser_action_b_);
365   extensions.push_back(browser_action_c_);
366
367   return AddAndVerifyExtensions(extensions);
368 }
369
370 const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
371     size_t index, const ExtensionToolbarModel* model) const {
372   return index < model->toolbar_items().size()
373              ? model->toolbar_items()[index].get()
374              : NULL;
375 }
376
377 const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
378     size_t index) const {
379   return GetExtensionAtIndex(index, toolbar_model_);
380 }
381
382 testing::AssertionResult ExtensionToolbarModelUnitTest::AddAndVerifyExtensions(
383     const ExtensionList& extensions) {
384   for (ExtensionList::const_iterator iter = extensions.begin();
385        iter != extensions.end(); ++iter) {
386     if (!AddExtension(*iter)) {
387       return testing::AssertionFailure() << "Failed to install extension: " <<
388           (*iter)->name();
389     }
390   }
391   return testing::AssertionSuccess();
392 }
393
394 // A basic test for extensions with browser actions showing up in the toolbar.
395 TEST_F(ExtensionToolbarModelUnitTest, BasicExtensionToolbarModelTest) {
396   Init();
397
398   // Load an extension with no browser action.
399   scoped_refptr<const Extension> extension1 =
400       GetActionExtension("no_action", NULL);
401   ASSERT_TRUE(AddExtension(extension1));
402
403   // This extension should not be in the model (has no browser action).
404   EXPECT_EQ(0u, observer()->inserted_count());
405   EXPECT_EQ(0u, num_toolbar_items());
406   EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
407
408   // Load an extension with a browser action.
409   scoped_refptr<const Extension> extension2 =
410       GetActionExtension("browser_action", manifest_keys::kBrowserAction);
411   ASSERT_TRUE(AddExtension(extension2));
412
413   // We should now find our extension in the model.
414   EXPECT_EQ(1u, observer()->inserted_count());
415   EXPECT_EQ(1u, num_toolbar_items());
416   EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
417
418   // Should be a no-op, but still fires the events.
419   toolbar_model()->MoveExtensionIcon(extension2->id(), 0);
420   EXPECT_EQ(1u, observer()->moved_count());
421   EXPECT_EQ(1u, num_toolbar_items());
422   EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
423
424   // Remove the extension and verify.
425   ASSERT_TRUE(RemoveExtension(extension2));
426   EXPECT_EQ(1u, observer()->removed_count());
427   EXPECT_EQ(0u, num_toolbar_items());
428   EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
429 }
430
431 // Test various different reorderings, removals, and reinsertions.
432 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarReorderAndReinsert) {
433   Init();
434
435   // Add the three browser action extensions.
436   ASSERT_TRUE(AddBrowserActionExtensions());
437
438   // Verify the three extensions are in the model in the proper order.
439   EXPECT_EQ(3u, num_toolbar_items());
440   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
441   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
442   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
443
444   // Order is now A, B, C. Let's put C first.
445   toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 0);
446   EXPECT_EQ(1u, observer()->moved_count());
447   EXPECT_EQ(3u, num_toolbar_items());
448   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
449   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
450   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
451
452   // Order is now C, A, B. Let's put A last.
453   toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 2);
454   EXPECT_EQ(2u, observer()->moved_count());
455   EXPECT_EQ(3u, num_toolbar_items());
456   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
457   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
458   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
459
460   // Order is now C, B, A. Let's remove B.
461   ASSERT_TRUE(RemoveExtension(browser_action_b()));
462   EXPECT_EQ(1u, observer()->removed_count());
463   EXPECT_EQ(2u, num_toolbar_items());
464   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
465   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
466
467   // Load extension B again.
468   ASSERT_TRUE(AddExtension(browser_action_b()));
469
470   // Extension B loaded again.
471   EXPECT_EQ(4u, observer()->inserted_count());
472   EXPECT_EQ(3u, num_toolbar_items());
473   // Make sure it gets its old spot in the list.
474   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
475
476   // Unload B again.
477   ASSERT_TRUE(RemoveExtension(browser_action_b()));
478   EXPECT_EQ(2u, observer()->removed_count());
479   EXPECT_EQ(2u, num_toolbar_items());
480
481   // Order is now C, A. Flip it.
482   toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 0);
483   EXPECT_EQ(3u, observer()->moved_count());
484   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
485   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
486
487   // Move A to the location it already occupies.
488   toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 0);
489   EXPECT_EQ(4u, observer()->moved_count());
490   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
491   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
492
493   // Order is now A, C. Remove C.
494   ASSERT_TRUE(RemoveExtension(browser_action_c()));
495   EXPECT_EQ(3u, observer()->removed_count());
496   EXPECT_EQ(1u, num_toolbar_items());
497   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
498
499   // Load extension C again.
500   ASSERT_TRUE(AddExtension(browser_action_c()));
501
502   // Extension C loaded again.
503   EXPECT_EQ(5u, observer()->inserted_count());
504   EXPECT_EQ(2u, num_toolbar_items());
505   // Make sure it gets its old spot in the list (at the very end).
506   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
507 }
508
509 // Test that order persists after unloading and disabling, but not across
510 // uninstallation.
511 TEST_F(ExtensionToolbarModelUnitTest,
512        ExtensionToolbarUnloadDisableAndUninstall) {
513   Init();
514
515   // Add the three browser action extensions.
516   ASSERT_TRUE(AddBrowserActionExtensions());
517
518   // Verify the three extensions are in the model in the proper order: A, B, C.
519   EXPECT_EQ(3u, num_toolbar_items());
520   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
521   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
522   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
523
524   // Unload B, then C, then A, and then reload C, then A, then B.
525   ASSERT_TRUE(RemoveExtension(browser_action_b()));
526   ASSERT_TRUE(RemoveExtension(browser_action_c()));
527   ASSERT_TRUE(RemoveExtension(browser_action_a()));
528   EXPECT_EQ(0u, num_toolbar_items());  // Sanity check: all gone?
529   ASSERT_TRUE(AddExtension(browser_action_c()));
530   ASSERT_TRUE(AddExtension(browser_action_a()));
531   ASSERT_TRUE(AddExtension(browser_action_b()));
532   EXPECT_EQ(3u, num_toolbar_items());  // Sanity check: all back?
533   EXPECT_EQ(0u, observer()->moved_count());
534
535   // Even though we unloaded and reloaded in a different order, the original
536   // order (A, B, C) should be preserved.
537   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
538   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
539   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
540
541   // Disabling extensions should also preserve order.
542   service()->DisableExtension(browser_action_b()->id(),
543                               Extension::DISABLE_USER_ACTION);
544   service()->DisableExtension(browser_action_c()->id(),
545                               Extension::DISABLE_USER_ACTION);
546   service()->DisableExtension(browser_action_a()->id(),
547                               Extension::DISABLE_USER_ACTION);
548   service()->EnableExtension(browser_action_c()->id());
549   service()->EnableExtension(browser_action_a()->id());
550   service()->EnableExtension(browser_action_b()->id());
551
552   // Make sure we still get the original A, B, C order.
553   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
554   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
555   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
556
557   // Move browser_action_b() to be first.
558   toolbar_model()->MoveExtensionIcon(browser_action_b()->id(), 0);
559   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
560
561   // Uninstall Extension B.
562   service()->UninstallExtension(browser_action_b()->id(),
563                                 UNINSTALL_REASON_FOR_TESTING,
564                                 base::Bind(&base::DoNothing),
565                                 NULL);  // Ignore error.
566   // List contains only A and C now. Validate that.
567   EXPECT_EQ(2u, num_toolbar_items());
568   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
569   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
570
571   ASSERT_TRUE(AddExtension(browser_action_b()));
572
573   // Make sure Extension B is _not_ first (its old position should have been
574   // forgotten at uninstall time). Order should be A, C, B.
575   EXPECT_EQ(3u, num_toolbar_items());
576   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
577   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
578   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
579 }
580
581 TEST_F(ExtensionToolbarModelUnitTest, ReorderOnPrefChange) {
582   Init();
583
584   // Add the three browser action extensions.
585   ASSERT_TRUE(AddBrowserActionExtensions());
586   EXPECT_EQ(3u, num_toolbar_items());
587
588   // Change the value of the toolbar preference.
589   ExtensionIdList new_order;
590   new_order.push_back(browser_action_c()->id());
591   new_order.push_back(browser_action_b()->id());
592   ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
593
594   // Verify order is changed.
595   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
596   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
597   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
598 }
599
600 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightMode) {
601   Init();
602
603   EXPECT_FALSE(toolbar_model()->HighlightExtensions(ExtensionIdList()));
604   EXPECT_EQ(0, observer()->highlight_mode_count());
605
606   // Add the three browser action extensions.
607   ASSERT_TRUE(AddBrowserActionExtensions());
608   EXPECT_EQ(3u, num_toolbar_items());
609
610   // Highlight one extension.
611   ExtensionIdList extension_ids;
612   extension_ids.push_back(browser_action_b()->id());
613   toolbar_model()->HighlightExtensions(extension_ids);
614   EXPECT_EQ(1, observer()->highlight_mode_count());
615   EXPECT_TRUE(toolbar_model()->is_highlighting());
616
617   EXPECT_EQ(1u, num_toolbar_items());
618   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
619
620   // Stop highlighting.
621   toolbar_model()->StopHighlighting();
622   EXPECT_EQ(0, observer()->highlight_mode_count());
623   EXPECT_FALSE(toolbar_model()->is_highlighting());
624
625   // Verify that the extensions are back to normal.
626   EXPECT_EQ(3u, num_toolbar_items());
627   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
628   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
629   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
630
631   // Call stop highlighting a second time (shouldn't be notified).
632   toolbar_model()->StopHighlighting();
633   EXPECT_EQ(0, observer()->highlight_mode_count());
634   EXPECT_FALSE(toolbar_model()->is_highlighting());
635
636   // Highlight all extensions.
637   extension_ids.clear();
638   extension_ids.push_back(browser_action_a()->id());
639   extension_ids.push_back(browser_action_b()->id());
640   extension_ids.push_back(browser_action_c()->id());
641   toolbar_model()->HighlightExtensions(extension_ids);
642   EXPECT_EQ(1, observer()->highlight_mode_count());
643   EXPECT_EQ(3u, num_toolbar_items());
644   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
645   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
646   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
647
648   // Highlight only extension b (shrink the highlight list).
649   extension_ids.clear();
650   extension_ids.push_back(browser_action_b()->id());
651   toolbar_model()->HighlightExtensions(extension_ids);
652   EXPECT_EQ(2, observer()->highlight_mode_count());
653   EXPECT_EQ(1u, num_toolbar_items());
654   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
655
656   // Highlight extensions a and b (grow the highlight list).
657   extension_ids.clear();
658   extension_ids.push_back(browser_action_a()->id());
659   extension_ids.push_back(browser_action_b()->id());
660   toolbar_model()->HighlightExtensions(extension_ids);
661   EXPECT_EQ(3, observer()->highlight_mode_count());
662   EXPECT_EQ(2u, num_toolbar_items());
663   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
664   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
665
666   // Highlight no extensions (empty the highlight list).
667   extension_ids.clear();
668   toolbar_model()->HighlightExtensions(extension_ids);
669   EXPECT_EQ(2, observer()->highlight_mode_count());
670   EXPECT_FALSE(toolbar_model()->is_highlighting());
671   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
672   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
673   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
674 }
675
676 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeRemove) {
677   Init();
678
679   // Add the three browser action extensions.
680   ASSERT_TRUE(AddBrowserActionExtensions());
681   EXPECT_EQ(3u, num_toolbar_items());
682
683   // Highlight two of the extensions.
684   ExtensionIdList extension_ids;
685   extension_ids.push_back(browser_action_a()->id());
686   extension_ids.push_back(browser_action_b()->id());
687   toolbar_model()->HighlightExtensions(extension_ids);
688   EXPECT_TRUE(toolbar_model()->is_highlighting());
689   EXPECT_EQ(1, observer()->highlight_mode_count());
690   EXPECT_EQ(2u, num_toolbar_items());
691
692   // Disable one of them - only one should remain highlighted.
693   service()->DisableExtension(browser_action_a()->id(),
694                               Extension::DISABLE_USER_ACTION);
695   EXPECT_TRUE(toolbar_model()->is_highlighting());
696   EXPECT_EQ(1u, num_toolbar_items());
697   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
698
699   // Uninstall the remaining highlighted extension. This should result in
700   // highlight mode exiting.
701   service()->UninstallExtension(browser_action_b()->id(),
702                                 UNINSTALL_REASON_FOR_TESTING,
703                                 base::Bind(&base::DoNothing),
704                                 NULL);  // Ignore error.
705   EXPECT_FALSE(toolbar_model()->is_highlighting());
706   EXPECT_EQ(0, observer()->highlight_mode_count());
707   EXPECT_EQ(1u, num_toolbar_items());
708   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
709
710   // Test that removing an unhighlighted extension still works.
711   // Reinstall extension b, and then highlight extension c.
712   ASSERT_TRUE(AddExtension(browser_action_b()));
713   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
714   extension_ids.clear();
715   extension_ids.push_back(browser_action_c()->id());
716   toolbar_model()->HighlightExtensions(extension_ids);
717   EXPECT_EQ(1, observer()->highlight_mode_count());
718   EXPECT_TRUE(toolbar_model()->is_highlighting());
719   EXPECT_EQ(1u, num_toolbar_items());
720   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
721
722   // Uninstalling b should not have visible impact.
723   service()->UninstallExtension(browser_action_b()->id(),
724                                 UNINSTALL_REASON_FOR_TESTING,
725                                 base::Bind(&base::DoNothing),
726                                 NULL);  // Ignore error.
727   EXPECT_TRUE(toolbar_model()->is_highlighting());
728   EXPECT_EQ(1, observer()->highlight_mode_count());
729   EXPECT_EQ(1u, num_toolbar_items());
730   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
731
732   // When we stop, only extension c should remain.
733   toolbar_model()->StopHighlighting();
734   EXPECT_FALSE(toolbar_model()->is_highlighting());
735   EXPECT_EQ(0, observer()->highlight_mode_count());
736   EXPECT_EQ(1u, num_toolbar_items());
737   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
738 }
739
740 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeAdd) {
741   Init();
742
743   // Add the three browser action extensions.
744   ASSERT_TRUE(AddBrowserActionExtensions());
745   EXPECT_EQ(3u, num_toolbar_items());
746
747   // Remove one (down to two).
748   ASSERT_TRUE(RemoveExtension(browser_action_c()));
749
750   // Highlight one of the two extensions.
751   ExtensionIdList extension_ids;
752   extension_ids.push_back(browser_action_a()->id());
753   toolbar_model()->HighlightExtensions(extension_ids);
754   EXPECT_TRUE(toolbar_model()->is_highlighting());
755   EXPECT_EQ(1u, num_toolbar_items());
756   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
757
758   // Adding a new extension should have no visible effect.
759   ASSERT_TRUE(AddExtension(browser_action_c()));
760   EXPECT_TRUE(toolbar_model()->is_highlighting());
761   EXPECT_EQ(1u, num_toolbar_items());
762   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
763
764   // When we stop highlighting, we should see the new extension show up.
765   toolbar_model()->StopHighlighting();
766   EXPECT_FALSE(toolbar_model()->is_highlighting());
767   EXPECT_EQ(3u, num_toolbar_items());
768   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
769   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
770   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
771 }
772
773 // Test that the extension toolbar maintains the proper size, even after a pref
774 // change.
775 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarSizeAfterPrefChange) {
776   Init();
777
778   // Add the three browser action extensions.
779   ASSERT_TRUE(AddBrowserActionExtensions());
780   EXPECT_EQ(3u, num_toolbar_items());
781
782   // Should be at max size.
783   EXPECT_TRUE(toolbar_model()->all_icons_visible());
784   EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
785   toolbar_model()->OnExtensionToolbarPrefChange();
786   // Should still be at max size.
787   EXPECT_TRUE(toolbar_model()->all_icons_visible());
788   EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
789 }
790
791 // Test that, in the absence of the extension-action-redesign switch, the
792 // model only contains extensions with browser actions.
793 TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesNoSwitch) {
794   Init();
795   ASSERT_TRUE(AddActionExtensions());
796
797   EXPECT_EQ(1u, num_toolbar_items());
798   EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
799 }
800
801 // Test that, with the extension-action-redesign switch, the model contains
802 // all types of extensions, except those which should not be displayed on the
803 // toolbar (like component extensions).
804 TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesSwitch) {
805   FeatureSwitch::ScopedOverride enable_redesign(
806       FeatureSwitch::extension_action_redesign(), true);
807   Init();
808   ASSERT_TRUE(AddActionExtensions());
809
810   // With the switch on, extensions with page actions and no action should also
811   // be displayed in the toolbar.
812   EXPECT_EQ(3u, num_toolbar_items());
813   EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
814   EXPECT_EQ(page_action(), GetExtensionAtIndex(1u));
815   EXPECT_EQ(no_action(), GetExtensionAtIndex(2u));
816 }
817
818 // Test that hiding actions on the toolbar results in their removal from the
819 // model when the redesign switch is not enabled.
820 TEST_F(ExtensionToolbarModelUnitTest,
821        ExtensionToolbarActionsVisibilityNoSwitch) {
822   Init();
823
824   ASSERT_TRUE(AddBrowserActionExtensions());
825   // Sanity check: Order should start as A B C.
826   EXPECT_EQ(3u, num_toolbar_items());
827   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
828   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
829   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
830
831   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
832
833   // By default, all actions should be visible.
834   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
835                   prefs, browser_action_a()->id()));
836   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
837                   prefs, browser_action_b()->id()));
838   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
839                   prefs, browser_action_c()->id()));
840
841   // Hiding an action should result in its removal from the toolbar.
842   ExtensionActionAPI::SetBrowserActionVisibility(
843       prefs, browser_action_b()->id(), false);
844   EXPECT_FALSE(ExtensionActionAPI::GetBrowserActionVisibility(
845                    prefs, browser_action_b()->id()));
846   // Thus, there should now only be two items on the toolbar - A and C.
847   EXPECT_EQ(2u, num_toolbar_items());
848   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
849   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
850
851   // Resetting the visibility to 'true' should result in the extension being
852   // added back at its original position.
853   ExtensionActionAPI::SetBrowserActionVisibility(
854       prefs, browser_action_b()->id(), true);
855   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
856                   prefs, browser_action_b()->id()));
857   // So the toolbar order should be A B C.
858   EXPECT_EQ(3u, num_toolbar_items());
859   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
860   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
861   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
862 }
863
864 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarIncognitoModeTest) {
865   Init();
866   ASSERT_TRUE(AddBrowserActionExtensions());
867
868   // Give two extensions incognito access.
869   // Note: We use ExtensionPrefs::SetIsIncognitoEnabled instead of
870   // util::SetIsIncognitoEnabled because the latter tries to reload the
871   // extension, which requries a filepath associated with the extension (and,
872   // for this test, reloading the extension is irrelevant to us).
873   ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
874   extension_prefs->SetIsIncognitoEnabled(browser_action_b()->id(), true);
875   extension_prefs->SetIsIncognitoEnabled(browser_action_c()->id(), true);
876
877   util::SetIsIncognitoEnabled(browser_action_b()->id(), profile(), true);
878   util::SetIsIncognitoEnabled(browser_action_c()->id(), profile(), true);
879
880   // Move C to the second index.
881   toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 1u);
882   // Set visible count to 2 so that c is overflowed. State is A C [B].
883   toolbar_model()->SetVisibleIconCount(2);
884   EXPECT_EQ(1u, observer()->moved_count());
885
886   // Get an incognito profile and toolbar.
887   ExtensionToolbarModel* incognito_model =
888       CreateToolbarModelForProfile(profile()->GetOffTheRecordProfile());
889
890   ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
891   EXPECT_EQ(0u, incognito_observer.moved_count());
892
893   // We should have two items, C and B, and the order should be preserved from
894   // the original model.
895   EXPECT_EQ(2u, incognito_model->toolbar_items().size());
896   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u, incognito_model));
897   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u, incognito_model));
898
899   // Extensions in the overflow menu in the regular toolbar should remain in
900   // overflow in the incognito toolbar. So, we should have C [B].
901   EXPECT_EQ(1u, incognito_model->visible_icon_count());
902   // The regular model should still have two icons visible.
903   EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
904
905   // Changing the incognito model size should not affect the regular model.
906   incognito_model->SetVisibleIconCount(0);
907   EXPECT_EQ(0u, incognito_model->visible_icon_count());
908   EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
909
910   // Expanding the incognito model to 2 should register as "all icons"
911   // since it is all of the incognito-enabled extensions.
912   incognito_model->SetVisibleIconCount(2u);
913   EXPECT_EQ(2u, incognito_model->visible_icon_count());
914   EXPECT_TRUE(incognito_model->all_icons_visible());
915
916   // Moving icons in the incognito toolbar should not affect the regular
917   // toolbar. Incognito currently has C B...
918   incognito_model->MoveExtensionIcon(browser_action_b()->id(), 0u);
919   // So now it should be B C...
920   EXPECT_EQ(1u, incognito_observer.moved_count());
921   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u, incognito_model));
922   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u, incognito_model));
923   // ... and the regular toolbar should be unaffected.
924   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
925   EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
926   EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
927
928   // Similarly, the observer for the regular model should not have received
929   // any updates.
930   EXPECT_EQ(1u, observer()->moved_count());
931
932   // And performing moves on the regular model should have no effect on the
933   // incognito model or its observers.
934   toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 2u);
935   EXPECT_EQ(2u, observer()->moved_count());
936   EXPECT_EQ(1u, incognito_observer.moved_count());
937 }
938
939 // Test that enabling extensions incognito with an active incognito profile
940 // works.
941 TEST_F(ExtensionToolbarModelUnitTest,
942        ExtensionToolbarIncognitoEnableExtension) {
943   Init();
944
945   const char* kManifest =
946       "{"
947       "  \"name\": \"%s\","
948       "  \"version\": \"1.0\","
949       "  \"manifest_version\": 2,"
950       "  \"browser_action\": {}"
951       "}";
952
953   // For this test, we need to have "real" extension files, because we need to
954   // be able to reload them during the incognito process. Since the toolbar
955   // needs to be notified of the reload, we need it this time (as opposed to
956   // above, where we simply set the prefs before the incognito bar was
957   // created.
958   TestExtensionDir dir1;
959   dir1.WriteManifest(base::StringPrintf(kManifest, "incognito1"));
960   TestExtensionDir dir2;
961   dir2.WriteManifest(base::StringPrintf(kManifest, "incognito2"));
962
963   TestExtensionDir* dirs[] = { &dir1, &dir2 };
964   const Extension* extensions[] = { nullptr, nullptr };
965   for (size_t i = 0; i < arraysize(dirs); ++i) {
966     // The extension id will be calculated from the file path; we need this to
967     // wait for the extension to load.
968     base::FilePath path_for_id =
969         base::MakeAbsoluteFilePath(dirs[i]->unpacked_path());
970     std::string id = crx_file::id_util::GenerateIdForPath(path_for_id);
971     TestExtensionRegistryObserver observer(registry(), id);
972     UnpackedInstaller::Create(service())->Load(dirs[i]->unpacked_path());
973     observer.WaitForExtensionLoaded();
974     extensions[i] = registry()->enabled_extensions().GetByID(id);
975     ASSERT_TRUE(extensions[i]);
976   }
977
978   // For readability, alias to A and B. Since we'll be reloading these
979   // extensions, we also can't rely on pointers.
980   std::string extension_a = extensions[0]->id();
981   std::string extension_b = extensions[1]->id();
982
983   // The first model should have both extensions visible.
984   EXPECT_EQ(2u, toolbar_model()->toolbar_items().size());
985   EXPECT_EQ(extension_a, GetExtensionAtIndex(0)->id());
986   EXPECT_EQ(extension_b, GetExtensionAtIndex(1)->id());
987
988   // Set the model to only show one extension, so the order is A [B].
989   toolbar_model()->SetVisibleIconCount(1u);
990
991   // Get an incognito profile and toolbar.
992   ExtensionToolbarModel* incognito_model =
993       CreateToolbarModelForProfile(profile()->GetOffTheRecordProfile());
994   ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
995
996   // Right now, no extensions are enabled in incognito mode.
997   EXPECT_EQ(0u, incognito_model->toolbar_items().size());
998
999   // Set extension b (which is overflowed) to be enabled in incognito. This
1000   // results in b reloading, so wait for it.
1001   {
1002     TestExtensionRegistryObserver observer(registry(), extension_b);
1003     util::SetIsIncognitoEnabled(extension_b, profile(), true);
1004     observer.WaitForExtensionLoaded();
1005   }
1006
1007   // Now, we should have one icon in the incognito bar. But, since B is
1008   // overflowed in the main bar, it shouldn't be visible.
1009   EXPECT_EQ(1u, incognito_model->toolbar_items().size());
1010   EXPECT_EQ(extension_b, GetExtensionAtIndex(0u, incognito_model)->id());
1011   EXPECT_EQ(0u, incognito_model->visible_icon_count());
1012
1013   // Also enable extension a for incognito (again, wait for the reload).
1014   {
1015     TestExtensionRegistryObserver observer(registry(), extension_a);
1016     util::SetIsIncognitoEnabled(extension_a, profile(), true);
1017     observer.WaitForExtensionLoaded();
1018   }
1019
1020   // Now, both extensions should be enabled in incognito mode. In addition, the
1021   // incognito toolbar should have expanded to show extension a (since it isn't
1022   // overflowed in the main bar).
1023   EXPECT_EQ(2u, incognito_model->toolbar_items().size());
1024   EXPECT_EQ(extension_a, GetExtensionAtIndex(0u, incognito_model)->id());
1025   EXPECT_EQ(extension_b, GetExtensionAtIndex(1u, incognito_model)->id());
1026   EXPECT_EQ(1u, incognito_model->visible_icon_count());
1027 }
1028
1029 // Test that hiding actions on the toolbar results in sending them to the
1030 // overflow menu when the redesign switch is enabled.
1031 TEST_F(ExtensionToolbarModelUnitTest,
1032        ExtensionToolbarActionsVisibilityWithSwitch) {
1033   FeatureSwitch::ScopedOverride enable_redesign(
1034       FeatureSwitch::extension_action_redesign(), true);
1035   Init();
1036
1037   // We choose to use all types of extensions here, since the misnamed
1038   // BrowserActionVisibility is now for toolbar visibility.
1039   ASSERT_TRUE(AddActionExtensions());
1040
1041   // For readability, alias extensions A B C.
1042   const Extension* extension_a = browser_action();
1043   const Extension* extension_b = page_action();
1044   const Extension* extension_c = no_action();
1045
1046   // Sanity check: Order should start as A B C, with all three visible.
1047   EXPECT_EQ(3u, num_toolbar_items());
1048   EXPECT_TRUE(toolbar_model()->all_icons_visible());
1049   EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
1050   EXPECT_EQ(extension_b, GetExtensionAtIndex(1u));
1051   EXPECT_EQ(extension_c, GetExtensionAtIndex(2u));
1052
1053   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
1054
1055   // By default, all actions should be visible.
1056   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
1057                   prefs, extension_a->id()));
1058   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
1059                   prefs, extension_c->id()));
1060   EXPECT_TRUE(ExtensionActionAPI::GetBrowserActionVisibility(
1061                   prefs, extension_b->id()));
1062
1063   // Hiding an action should result in it being sent to the overflow menu.
1064   ExtensionActionAPI::SetBrowserActionVisibility(
1065       prefs, extension_b->id(), false);
1066
1067   // Thus, the order should be A C B, with B in the overflow.
1068   EXPECT_EQ(3u, num_toolbar_items());
1069   EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
1070   EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
1071   EXPECT_EQ(extension_c, GetExtensionAtIndex(1u));
1072   EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
1073
1074   // Hiding an extension's action should result in it being sent to the overflow
1075   // as well, but as the _first_ extension in the overflow.
1076   ExtensionActionAPI::SetBrowserActionVisibility(
1077       prefs, extension_a->id(), false);
1078   // Thus, the order should be C A B, with A and B in the overflow.
1079   EXPECT_EQ(3u, num_toolbar_items());
1080   EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
1081   EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
1082   EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
1083   EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
1084
1085   // Resetting A's visibility to true should send it back to the visible icons
1086   // (and should grow visible icons by 1), but it should be added to the end of
1087   // the visible icon list (not to its original position).
1088   ExtensionActionAPI::SetBrowserActionVisibility(
1089       prefs, extension_a->id(), true);
1090   // So order is C A B, with only B in the overflow.
1091   EXPECT_EQ(3u, num_toolbar_items());
1092   EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
1093   EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
1094   EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
1095   EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
1096
1097   // Resetting B to be visible should make the order C A B, with no overflow.
1098   ExtensionActionAPI::SetBrowserActionVisibility(
1099       prefs, extension_b->id(), true);
1100   EXPECT_EQ(3u, num_toolbar_items());
1101   EXPECT_TRUE(toolbar_model()->all_icons_visible());
1102   EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
1103   EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
1104   EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
1105 }
1106
1107 // Test that toolbar actions can pop themselves out of overflow if they want
1108 // to act on a given web contents.
1109 TEST_F(ExtensionToolbarModelUnitTest, ToolbarActionsPopOutToAct) {
1110   // Extensions popping themselves out to act is part of the toolbar redesign,
1111   // and hidden behind a flag.
1112   FeatureSwitch::ScopedOverride enable_redesign(
1113       FeatureSwitch::extension_action_redesign(), true);
1114   Init();
1115   TestWebContentsFactory web_contents_factory;
1116
1117   ASSERT_TRUE(AddActionExtensions());
1118
1119   // We should start in the order of "browser action" "page action" "no action"
1120   // and have all extensions visible.
1121   EXPECT_EQ(3u, num_toolbar_items());
1122   EXPECT_TRUE(toolbar_model()->all_icons_visible());
1123   EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
1124   EXPECT_EQ(page_action(), GetExtensionAtIndex(1u));
1125   EXPECT_EQ(no_action(), GetExtensionAtIndex(2u));
1126
1127   // Shrink the model to only show one action, and move the page action to the
1128   // end.
1129   toolbar_model()->SetVisibleIconCount(1);
1130   toolbar_model()->MoveExtensionIcon(page_action()->id(), 2u);
1131
1132   // Quickly verify that the move/visible count worked.
1133   EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
1134   EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
1135   EXPECT_EQ(no_action(), GetExtensionAtIndex(1u));
1136   EXPECT_EQ(page_action(), GetExtensionAtIndex(2u));
1137
1138   // Create two test web contents, and a session tab helper for each. We need
1139   // a session tab helper, since we rely on tab ids.
1140   content::WebContents* web_contents =
1141       web_contents_factory.CreateWebContents(profile());
1142   ASSERT_TRUE(web_contents);
1143   SessionTabHelper::CreateForWebContents(web_contents);
1144   content::WebContents* second_web_contents =
1145       web_contents_factory.CreateWebContents(profile());
1146   ASSERT_TRUE(second_web_contents);
1147   SessionTabHelper::CreateForWebContents(second_web_contents);
1148
1149   // Find the tab ids, ensure that the two web contents have different ids, and
1150   // verify that neither is -1 (invalid).
1151   int tab_id = SessionTabHelper::IdForTab(web_contents);
1152   int second_tab_id = SessionTabHelper::IdForTab(second_web_contents);
1153   EXPECT_NE(tab_id, second_tab_id);
1154   EXPECT_NE(-1, second_tab_id);
1155   EXPECT_NE(-1, tab_id);
1156
1157   // First, check the model order for the first tab. Since we haven't changed
1158   // anything (i.e., no extensions want to act), this should be the same as we
1159   // left it: "browser action", "no action", "page action", with only one
1160   // visible.
1161   ExtensionList tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1162   ASSERT_EQ(3u, tab_order.size());
1163   EXPECT_EQ(browser_action(), tab_order[0]);
1164   EXPECT_EQ(no_action(), tab_order[1]);
1165   EXPECT_EQ(page_action(), tab_order[2]);
1166   EXPECT_EQ(1u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1167   // And we should have no notifications to reorder the toolbar.
1168   EXPECT_EQ(0u, observer()->reorder_count());
1169
1170   // Make "page action" want to act by making it's page action visible on the
1171   // first tab, and notify the API of the change.
1172   ExtensionActionManager* action_manager =
1173       ExtensionActionManager::Get(profile());
1174   ExtensionAction* action = action_manager->GetExtensionAction(*page_action());
1175   ASSERT_TRUE(action);
1176   action->SetIsVisible(tab_id, true);
1177   ExtensionActionAPI* extension_action_api = ExtensionActionAPI::Get(profile());
1178   extension_action_api->NotifyChange(action, web_contents, profile());
1179
1180   // This should result in "page action" being popped out of the overflow menu.
1181   // This has two visible effects:
1182   // - page action should move to the second index (the one right after the last
1183   //   originally-visible).
1184   // - The visible count should increase by one (so page action is visible).
1185   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1186   ASSERT_EQ(3u, tab_order.size());
1187   EXPECT_EQ(browser_action(), tab_order[0]);
1188   EXPECT_EQ(page_action(), tab_order[1]);
1189   EXPECT_EQ(no_action(), tab_order[2]);
1190   EXPECT_EQ(2u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1191   // We should also have been told to reorder the toolbar.
1192   EXPECT_EQ(1u, observer()->reorder_count());
1193
1194   // This should not have any effect on the second tab, which should still have
1195   // the original order and visible count.
1196   tab_order = toolbar_model()->GetItemOrderForTab(second_web_contents);
1197   ASSERT_EQ(3u, tab_order.size());
1198   EXPECT_EQ(browser_action(), tab_order[0]);
1199   EXPECT_EQ(no_action(), tab_order[1]);
1200   EXPECT_EQ(page_action(), tab_order[2]);
1201   EXPECT_EQ(1u,
1202             toolbar_model()->GetVisibleIconCountForTab(second_web_contents));
1203
1204   // Now, set the action to be hidden again, and notify of the change.
1205   action->SetIsVisible(tab_id, false);
1206   extension_action_api->NotifyChange(action, web_contents, profile());
1207   // The order and visible count should return to normal (the page action should
1208   // move back to its original index in overflow). So, order should be "browser
1209   // action", "no action", "page action".
1210   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1211   ASSERT_EQ(3u, tab_order.size());
1212   EXPECT_EQ(browser_action(), tab_order[0]);
1213   EXPECT_EQ(no_action(), tab_order[1]);
1214   EXPECT_EQ(page_action(), tab_order[2]);
1215   EXPECT_EQ(1u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1216   // This should also result in a reorder.
1217   EXPECT_EQ(2u, observer()->reorder_count());
1218
1219   // Move page action to the first index (so it's naturally visible), and make
1220   // it want to act.
1221   toolbar_model()->MoveExtensionIcon(page_action()->id(), 0u);
1222   action->SetIsVisible(tab_id, true);
1223   extension_action_api->NotifyChange(action, web_contents, profile());
1224   // Since the action is already visible, this should have no effect - the order
1225   // and visible count should remain unchanged. Order is "page action", "browser
1226   // action", "no action".
1227   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1228   ASSERT_EQ(3u, tab_order.size());
1229   EXPECT_EQ(page_action(), tab_order[0]);
1230   EXPECT_EQ(browser_action(), tab_order[1]);
1231   EXPECT_EQ(no_action(), tab_order[2]);
1232   EXPECT_EQ(1u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1233
1234   // We should still be able to increase the size of the model, and to move the
1235   // page action.
1236   toolbar_model()->SetVisibleIconCount(2);
1237   toolbar_model()->MoveExtensionIcon(page_action()->id(), 1u);
1238   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1239   ASSERT_EQ(3u, tab_order.size());
1240   EXPECT_EQ(browser_action(), tab_order[0]);
1241   EXPECT_EQ(page_action(), tab_order[1]);
1242   EXPECT_EQ(no_action(), tab_order[2]);
1243   EXPECT_EQ(2u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1244
1245   // Neither of the above operations should have precipitated a reorder.
1246   EXPECT_EQ(2u, observer()->reorder_count());
1247
1248   // If we moved the page action, the move should remain in effect even after
1249   // the action no longer wants to act.
1250   action->SetIsVisible(tab_id, false);
1251   extension_action_api->NotifyChange(action, web_contents, profile());
1252   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1253   ASSERT_EQ(3u, tab_order.size());
1254   EXPECT_EQ(browser_action(), tab_order[0]);
1255   EXPECT_EQ(page_action(), tab_order[1]);
1256   EXPECT_EQ(no_action(), tab_order[2]);
1257   EXPECT_EQ(2u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1258   // The above change should *not* require a reorder, because the extension is
1259   // in a new, visible spot and doesn't need to change its position.
1260   EXPECT_EQ(2u, observer()->reorder_count());
1261
1262   // Test the edge case of having no icons visible.
1263   toolbar_model()->SetVisibleIconCount(0);
1264   EXPECT_EQ(0u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1265   action->SetIsVisible(tab_id, true);
1266   extension_action_api->NotifyChange(action, web_contents, profile());
1267   tab_order = toolbar_model()->GetItemOrderForTab(web_contents);
1268   ASSERT_EQ(3u, tab_order.size());
1269   EXPECT_EQ(page_action(), tab_order[0]);
1270   EXPECT_EQ(browser_action(), tab_order[1]);
1271   EXPECT_EQ(no_action(), tab_order[2]);
1272   EXPECT_EQ(1u, toolbar_model()->GetVisibleIconCountForTab(web_contents));
1273   EXPECT_EQ(3u, observer()->reorder_count());
1274 }
1275
1276 }  // namespace extensions