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.
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"
37 #include "ui/aura/env.h"
40 namespace extensions {
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.
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 {
61 TestWebContentsFactory();
62 ~TestWebContentsFactory();
64 // Creates a new WebContents with the given |context|, and returns it.
65 content::WebContents* CreateWebContents(content::BrowserContext* context);
68 // The test factory (and friends) for creating test web contents.
69 scoped_ptr<content::RenderViewHostTestEnabler> rvh_enabler_;
71 // The vector of web contents that this class created.
72 ScopedVector<content::WebContents> web_contents_;
74 DISALLOW_COPY_AND_ASSIGN(TestWebContentsFactory);
77 TestWebContentsFactory::TestWebContentsFactory()
78 : rvh_enabler_(new content::RenderViewHostTestEnabler()) {
80 aura::Env::CreateInstance(true);
84 TestWebContentsFactory::~TestWebContentsFactory() {
85 web_contents_.clear();
86 // Let any posted tasks for web contents deletion run.
87 base::RunLoop().RunUntilIdle();
89 // Let any posted tasks for RenderProcess/ViewHost deletion run.
90 base::RunLoop().RunUntilIdle();
93 aura::Env::DeleteInstance();
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();
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));
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);
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
131 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile))->SetReady();
132 // Run tasks posted to TestExtensionSystem.
133 base::RunLoop().RunUntilIdle();
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
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");
149 manifest.Set(action_key, DictionaryBuilder().Pass());
151 return ExtensionBuilder().SetManifest(manifest.Pass())
152 .SetID(crx_file::id_util::GenerateId(name))
156 // A simple observer that tracks the number of times certain events occur.
157 class ExtensionToolbarModelTestObserver
158 : public ExtensionToolbarModel::Observer {
160 explicit ExtensionToolbarModelTestObserver(ExtensionToolbarModel* model);
161 ~ExtensionToolbarModelTestObserver() override;
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_; }
170 // ExtensionToolbarModel::Observer:
171 void ToolbarExtensionAdded(const Extension* extension, int index) override {
175 void ToolbarExtensionRemoved(const Extension* extension) override {
179 void ToolbarExtensionMoved(const Extension* extension, int index) override {
183 void ToolbarExtensionUpdated(const Extension* extension) override {}
185 bool ShowExtensionActionPopup(const Extension* extension,
186 bool grant_active_tab) override {
190 void ToolbarVisibleCountChanged() override {}
192 void ToolbarHighlightModeChanged(bool is_highlighting) override {
193 // Add one if highlighting, subtract one if not.
194 highlight_mode_count_ += is_highlighting ? 1 : -1;
197 void OnToolbarReorderNecessary(content::WebContents* web_contents) override {
201 Browser* GetBrowser() override { return NULL; }
203 ExtensionToolbarModel* model_;
205 size_t inserted_count_;
206 size_t removed_count_;
208 // Int because it could become negative (if something goes wrong).
209 int highlight_mode_count_;
210 size_t reorder_count_;
213 ExtensionToolbarModelTestObserver::ExtensionToolbarModelTestObserver(
214 ExtensionToolbarModel* model) : model_(model),
218 highlight_mode_count_(0),
220 model_->AddObserver(this);
223 ExtensionToolbarModelTestObserver::~ExtensionToolbarModelTestObserver() {
224 model_->RemoveObserver(this);
229 class ExtensionToolbarModelUnitTest : public ExtensionServiceTestBase {
231 // Initialize the ExtensionService, ExtensionToolbarModel, and
235 void TearDown() override;
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;
243 // Adds three extensions, all with browser actions.
244 testing::AssertionResult AddBrowserActionExtensions() WARN_UNUSED_RESULT;
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;
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
254 const Extension* GetExtensionAtIndex(
255 size_t index, const ExtensionToolbarModel* model) const;
256 const Extension* GetExtensionAtIndex(size_t index) const;
258 ExtensionToolbarModel* toolbar_model() { return toolbar_model_; }
260 const ExtensionToolbarModelTestObserver* observer() const {
261 return model_observer_.get();
263 size_t num_toolbar_items() const {
264 return toolbar_model_->toolbar_items().size();
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();
272 const Extension* page_action() const { return page_action_extension_.get(); }
273 const Extension* no_action() const { return no_action_extension_.get(); }
276 // Verifies that all extensions in |extensions| are added successfully.
277 testing::AssertionResult AddAndVerifyExtensions(
278 const ExtensionList& extensions);
280 // The toolbar model associated with the testing profile.
281 ExtensionToolbarModel* toolbar_model_;
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_;
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_;
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_;
298 void ExtensionToolbarModelUnitTest::Init() {
299 InitializeEmptyExtensionService();
300 toolbar_model_ = CreateToolbarModelForProfile(profile());
301 model_observer_.reset(new ExtensionToolbarModelTestObserver(toolbar_model_));
304 void ExtensionToolbarModelUnitTest::TearDown() {
305 model_observer_.reset();
306 ExtensionServiceTestBase::TearDown();
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!";
315 service()->AddExtension(extension.get());
316 if (!registry()->enabled_extensions().GetByID(extension->id())) {
317 return testing::AssertionFailure() << "Failed to install extension: " <<
320 return testing::AssertionSuccess();
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() <<
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: " <<
335 return testing::AssertionSuccess();
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);
345 ExtensionList extensions;
346 extensions.push_back(browser_action_extension_);
347 extensions.push_back(page_action_extension_);
348 extensions.push_back(no_action_extension_);
350 return AddAndVerifyExtensions(extensions);
353 testing::AssertionResult
354 ExtensionToolbarModelUnitTest::AddBrowserActionExtensions() {
356 GetActionExtension("browser_actionA", manifest_keys::kBrowserAction);
358 GetActionExtension("browser_actionB", manifest_keys::kBrowserAction);
360 GetActionExtension("browser_actionC", manifest_keys::kBrowserAction);
362 ExtensionList extensions;
363 extensions.push_back(browser_action_a_);
364 extensions.push_back(browser_action_b_);
365 extensions.push_back(browser_action_c_);
367 return AddAndVerifyExtensions(extensions);
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()
377 const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
378 size_t index) const {
379 return GetExtensionAtIndex(index, toolbar_model_);
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: " <<
391 return testing::AssertionSuccess();
394 // A basic test for extensions with browser actions showing up in the toolbar.
395 TEST_F(ExtensionToolbarModelUnitTest, BasicExtensionToolbarModelTest) {
398 // Load an extension with no browser action.
399 scoped_refptr<const Extension> extension1 =
400 GetActionExtension("no_action", NULL);
401 ASSERT_TRUE(AddExtension(extension1));
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));
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));
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));
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));
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));
431 // Test various different reorderings, removals, and reinsertions.
432 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarReorderAndReinsert) {
435 // Add the three browser action extensions.
436 ASSERT_TRUE(AddBrowserActionExtensions());
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));
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));
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));
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));
467 // Load extension B again.
468 ASSERT_TRUE(AddExtension(browser_action_b()));
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));
477 ASSERT_TRUE(RemoveExtension(browser_action_b()));
478 EXPECT_EQ(2u, observer()->removed_count());
479 EXPECT_EQ(2u, num_toolbar_items());
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));
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));
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));
499 // Load extension C again.
500 ASSERT_TRUE(AddExtension(browser_action_c()));
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));
509 // Test that order persists after unloading and disabling, but not across
511 TEST_F(ExtensionToolbarModelUnitTest,
512 ExtensionToolbarUnloadDisableAndUninstall) {
515 // Add the three browser action extensions.
516 ASSERT_TRUE(AddBrowserActionExtensions());
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));
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());
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));
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());
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));
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));
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));
571 ASSERT_TRUE(AddExtension(browser_action_b()));
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));
581 TEST_F(ExtensionToolbarModelUnitTest, ReorderOnPrefChange) {
584 // Add the three browser action extensions.
585 ASSERT_TRUE(AddBrowserActionExtensions());
586 EXPECT_EQ(3u, num_toolbar_items());
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);
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));
600 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightMode) {
603 EXPECT_FALSE(toolbar_model()->HighlightExtensions(ExtensionIdList()));
604 EXPECT_EQ(0, observer()->highlight_mode_count());
606 // Add the three browser action extensions.
607 ASSERT_TRUE(AddBrowserActionExtensions());
608 EXPECT_EQ(3u, num_toolbar_items());
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());
617 EXPECT_EQ(1u, num_toolbar_items());
618 EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
620 // Stop highlighting.
621 toolbar_model()->StopHighlighting();
622 EXPECT_EQ(0, observer()->highlight_mode_count());
623 EXPECT_FALSE(toolbar_model()->is_highlighting());
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));
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());
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));
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));
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));
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));
676 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeRemove) {
679 // Add the three browser action extensions.
680 ASSERT_TRUE(AddBrowserActionExtensions());
681 EXPECT_EQ(3u, num_toolbar_items());
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());
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));
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));
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));
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));
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));
740 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeAdd) {
743 // Add the three browser action extensions.
744 ASSERT_TRUE(AddBrowserActionExtensions());
745 EXPECT_EQ(3u, num_toolbar_items());
747 // Remove one (down to two).
748 ASSERT_TRUE(RemoveExtension(browser_action_c()));
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));
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));
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));
773 // Test that the extension toolbar maintains the proper size, even after a pref
775 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarSizeAfterPrefChange) {
778 // Add the three browser action extensions.
779 ASSERT_TRUE(AddBrowserActionExtensions());
780 EXPECT_EQ(3u, num_toolbar_items());
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());
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) {
795 ASSERT_TRUE(AddActionExtensions());
797 EXPECT_EQ(1u, num_toolbar_items());
798 EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
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);
808 ASSERT_TRUE(AddActionExtensions());
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));
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) {
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));
831 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
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()));
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));
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));
864 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarIncognitoModeTest) {
866 ASSERT_TRUE(AddBrowserActionExtensions());
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);
877 util::SetIsIncognitoEnabled(browser_action_b()->id(), profile(), true);
878 util::SetIsIncognitoEnabled(browser_action_c()->id(), profile(), true);
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());
886 // Get an incognito profile and toolbar.
887 ExtensionToolbarModel* incognito_model =
888 CreateToolbarModelForProfile(profile()->GetOffTheRecordProfile());
890 ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
891 EXPECT_EQ(0u, incognito_observer.moved_count());
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));
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());
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());
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());
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));
928 // Similarly, the observer for the regular model should not have received
930 EXPECT_EQ(1u, observer()->moved_count());
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());
939 // Test that enabling extensions incognito with an active incognito profile
941 TEST_F(ExtensionToolbarModelUnitTest,
942 ExtensionToolbarIncognitoEnableExtension) {
945 const char* kManifest =
948 " \"version\": \"1.0\","
949 " \"manifest_version\": 2,"
950 " \"browser_action\": {}"
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
958 TestExtensionDir dir1;
959 dir1.WriteManifest(base::StringPrintf(kManifest, "incognito1"));
960 TestExtensionDir dir2;
961 dir2.WriteManifest(base::StringPrintf(kManifest, "incognito2"));
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]);
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();
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());
988 // Set the model to only show one extension, so the order is A [B].
989 toolbar_model()->SetVisibleIconCount(1u);
991 // Get an incognito profile and toolbar.
992 ExtensionToolbarModel* incognito_model =
993 CreateToolbarModelForProfile(profile()->GetOffTheRecordProfile());
994 ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
996 // Right now, no extensions are enabled in incognito mode.
997 EXPECT_EQ(0u, incognito_model->toolbar_items().size());
999 // Set extension b (which is overflowed) to be enabled in incognito. This
1000 // results in b reloading, so wait for it.
1002 TestExtensionRegistryObserver observer(registry(), extension_b);
1003 util::SetIsIncognitoEnabled(extension_b, profile(), true);
1004 observer.WaitForExtensionLoaded();
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());
1013 // Also enable extension a for incognito (again, wait for the reload).
1015 TestExtensionRegistryObserver observer(registry(), extension_a);
1016 util::SetIsIncognitoEnabled(extension_a, profile(), true);
1017 observer.WaitForExtensionLoaded();
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());
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);
1037 // We choose to use all types of extensions here, since the misnamed
1038 // BrowserActionVisibility is now for toolbar visibility.
1039 ASSERT_TRUE(AddActionExtensions());
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();
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));
1053 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
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()));
1063 // Hiding an action should result in it being sent to the overflow menu.
1064 ExtensionActionAPI::SetBrowserActionVisibility(
1065 prefs, extension_b->id(), false);
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));
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));
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));
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));
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);
1115 TestWebContentsFactory web_contents_factory;
1117 ASSERT_TRUE(AddActionExtensions());
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));
1127 // Shrink the model to only show one action, and move the page action to the
1129 toolbar_model()->SetVisibleIconCount(1);
1130 toolbar_model()->MoveExtensionIcon(page_action()->id(), 2u);
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));
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);
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);
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
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());
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());
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());
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]);
1202 toolbar_model()->GetVisibleIconCountForTab(second_web_contents));
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());
1219 // Move page action to the first index (so it's naturally visible), and make
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));
1234 // We should still be able to increase the size of the model, and to move the
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));
1245 // Neither of the above operations should have precipitated a reorder.
1246 EXPECT_EQ(2u, observer()->reorder_count());
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());
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());
1276 } // namespace extensions