summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorapacible <apacible@chromium.org>2015-08-17 22:14:14 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-18 05:14:45 +0000
commitf9cfc4d02e387612c90385a5998af884975942a5 (patch)
tree6709d9d54b51621fc2d70ef93d3bd3c969326425
parent1a9a9ce3b6bec20af9c0bcc9427f654d53697abe (diff)
downloadchromium_src-f9cfc4d02e387612c90385a5998af884975942a5.zip
chromium_src-f9cfc4d02e387612c90385a5998af884975942a5.tar.gz
chromium_src-f9cfc4d02e387612c90385a5998af884975942a5.tar.bz2
Support Component Actions in the toolbar.
This change supports component actions in the toolbar, which previously only supported extension actions. ExtensionToolbarModel now keep track of extension ids (strings) rather than Extensions. This change also fixes the blank placement for the Media Router Action icon in new windows. BUG=511292, 511427, 511302 Review URL: https://codereview.chromium.org/1241063003 Cr-Commit-Position: refs/heads/master@{#343808}
-rw-r--r--chrome/browser/extensions/api/extension_action/browser_action_apitest.cc1
-rw-r--r--chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc4
-rw-r--r--chrome/browser/extensions/api/extension_action/extension_action_api.cc8
-rw-r--r--chrome/browser/extensions/browser_context_keyed_service_factories.cc4
-rw-r--r--chrome/browser/extensions/extension_action_test_util.cc57
-rw-r--r--chrome/browser/extensions/extension_action_test_util.h8
-rw-r--r--chrome/browser/extensions/extension_message_bubble_controller.cc8
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.cc753
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.h277
-rw-r--r--chrome/browser/extensions/extension_toolbar_model_factory.cc59
-rw-r--r--chrome/browser/extensions/extension_toolbar_model_unittest.cc1210
-rw-r--r--chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm8
-rw-r--r--chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm2
-rw-r--r--chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.cc7
-rw-r--r--chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc10
-rw-r--r--chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h6
-rw-r--r--chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc58
-rw-r--r--chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc42
-rw-r--r--chrome/browser/ui/toolbar/component_toolbar_actions_factory.h20
-rw-r--r--chrome/browser/ui/toolbar/media_router_action.cc28
-rw-r--r--chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.cc32
-rw-r--r--chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h32
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_bar.cc118
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_bar.h39
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc2
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.h8
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_model.cc844
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_model.h311
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_model_factory.cc55
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_model_factory.h (renamed from chrome/browser/extensions/extension_toolbar_model_factory.h)24
-rw-r--r--chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc1369
-rw-r--r--chrome/browser/ui/toolbar/wrench_menu_model.cc20
-rw-r--r--chrome/browser/ui/toolbar/wrench_menu_model.h4
-rw-r--r--chrome/browser/ui/views/toolbar/browser_actions_container.cc6
-rw-r--r--chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc28
-rw-r--r--chrome/browser/ui/views/toolbar/chevron_menu_button.cc1
-rw-r--r--chrome/chrome_browser_extensions.gypi8
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi4
-rw-r--r--chrome/common/chrome_switches.cc2
-rw-r--r--tools/metrics/histograms/histograms.xml32
41 files changed, 2910 insertions, 2601 deletions
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
index 32eeae5..0dfc4a8 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -10,7 +10,6 @@
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
index 8d1b322..66cfc28 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -7,7 +7,6 @@
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
@@ -15,6 +14,7 @@
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/notification_service.h"
@@ -102,7 +102,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TestOpenPopup) {
ui::PAGE_TRANSITION_TYPED, false)));
// Hide all the buttons to test that it opens even when the browser action
// is in the overflow bucket.
- extensions::ExtensionToolbarModel::Get(profile())->SetVisibleIconCount(0);
+ ToolbarActionsModel::Get(profile())->SetVisibleIconCount(0);
frame_observer.Wait();
}
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 8c151a9..0736edff 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -13,7 +13,6 @@
#include "chrome/browser/extensions/active_script_controller.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
@@ -23,6 +22,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/extensions/api/extension_action/action_info.h"
#include "content/public/browser/notification_service.h"
#include "extensions/browser/event_router.h"
@@ -228,9 +228,9 @@ bool ExtensionActionAPI::ShowExtensionActionPopup(
return browser->window()->GetLocationBar()->ShowPageActionPopup(
extension, grant_active_tab_permissions);
} else {
- return ExtensionToolbarModel::Get(browser->profile())->
- ShowExtensionActionPopup(
- extension, browser, grant_active_tab_permissions);
+ return ToolbarActionsModel::Get(browser->profile())
+ ->ShowToolbarActionPopup(extension->id(), browser,
+ grant_active_tab_permissions);
}
}
diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
index f97a4be..934390d 100644
--- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
@@ -47,7 +47,6 @@
#include "chrome/browser/extensions/extension_gcm_app_handler.h"
#include "chrome/browser/extensions/extension_storage_monitor_factory.h"
#include "chrome/browser/extensions/extension_system_factory.h"
-#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
#include "chrome/browser/extensions/extension_web_ui_override_registrar.h"
#include "chrome/browser/extensions/install_tracker_factory.h"
#include "chrome/browser/extensions/install_verifier_factory.h"
@@ -56,6 +55,7 @@
#include "chrome/browser/extensions/token_cache/token_cache_service_factory.h"
#include "chrome/browser/extensions/warning_badge_service_factory.h"
#include "chrome/browser/speech/extension_api/tts_extension_api.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
@@ -89,7 +89,6 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
extensions::ExtensionGarbageCollectorFactory::GetInstance();
extensions::ExtensionStorageMonitorFactory::GetInstance();
extensions::ExtensionSystemFactory::GetInstance();
- extensions::ExtensionToolbarModelFactory::GetInstance();
extensions::ExtensionWebUIOverrideRegistrar::GetFactoryInstance();
extensions::FeedbackPrivateAPI::GetFactoryInstance();
extensions::FontSettingsAPI::GetFactoryInstance();
@@ -141,6 +140,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
file_manager::EventRouterFactory::GetInstance();
#endif
TokenCacheServiceFactory::GetInstance();
+ ToolbarActionsModelFactory::GetInstance();
extensions::ExtensionGCMAppHandler::GetFactoryInstance();
}
diff --git a/chrome/browser/extensions/extension_action_test_util.cc b/chrome/browser/extensions/extension_action_test_util.cc
index 6a6eddc..1a9de97 100644
--- a/chrome/browser/extensions/extension_action_test_util.cc
+++ b/chrome/browser/extensions/extension_action_test_util.cc
@@ -8,15 +8,16 @@
#include "base/run_loop.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
-#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
#include "chrome/browser/extensions/location_bar_controller.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
#include "components/crx_file/id_util.h"
#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/feature_switch.h"
@@ -35,7 +36,7 @@ size_t GetPageActionCount(content::WebContents* web_contents,
int tab_id = SessionTabHelper::IdForTab(web_contents);
// Page actions are either stored in the location bar (and provided by the
// LocationBarController), or in the main toolbar (and provided by the
- // ExtensionToolbarModel), depending on whether or not the extension action
+ // ToolbarActionsModel), depending on whether or not the extension action
// redesign is enabled.
if (!FeatureSwitch::extension_action_redesign()->IsEnabled()) {
std::vector<ExtensionAction*> page_actions =
@@ -51,44 +52,50 @@ size_t GetPageActionCount(content::WebContents* web_contents,
}
}
} else {
- ExtensionToolbarModel* toolbar_model =
- ExtensionToolbarModel::Get(
- Profile::FromBrowserContext(web_contents->GetBrowserContext()));
- const ExtensionList& toolbar_extensions = toolbar_model->toolbar_items();
+ Profile* profile =
+ Profile::FromBrowserContext(web_contents->GetBrowserContext());
+ ToolbarActionsModel* toolbar_model = ToolbarActionsModel::Get(profile);
+ const std::vector<ToolbarActionsModel::ToolbarItem>& toolbar_items =
+ toolbar_model->toolbar_items();
ExtensionActionManager* action_manager =
ExtensionActionManager::Get(web_contents->GetBrowserContext());
- for (ExtensionList::const_iterator iter = toolbar_extensions.begin();
- iter != toolbar_extensions.end(); ++iter) {
- ExtensionAction* extension_action = action_manager->GetPageAction(**iter);
- if (extension_action &&
- (!only_count_visible || extension_action->GetIsVisible(tab_id)))
- ++count;
+ for (const ToolbarActionsModel::ToolbarItem& item : toolbar_items) {
+ if (item.type == ToolbarActionsModel::EXTENSION_ACTION) {
+ const Extension* extension =
+ ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
+ item.id);
+ ExtensionAction* extension_action =
+ action_manager->GetPageAction(*extension);
+ if (extension_action &&
+ (!only_count_visible || extension_action->GetIsVisible(tab_id)))
+ ++count;
+ }
}
}
return count;
}
-// Creates a new ExtensionToolbarModel for the given |context|.
+// Creates a new ToolbarActionsModel for the given |context|.
scoped_ptr<KeyedService> BuildToolbarModel(content::BrowserContext* context) {
- return make_scoped_ptr(new extensions::ExtensionToolbarModel(
- Profile::FromBrowserContext(context),
- extensions::ExtensionPrefs::Get(context)));
+ return make_scoped_ptr(
+ new ToolbarActionsModel(Profile::FromBrowserContext(context),
+ extensions::ExtensionPrefs::Get(context)));
}
-// Creates a new ExtensionToolbarModel for the given profile, optionally
+// Creates a new ToolbarActionsModel for the given profile, optionally
// triggering the extension system's ready signal.
-ExtensionToolbarModel* CreateToolbarModelImpl(Profile* profile,
- bool wait_for_ready) {
- ExtensionToolbarModel* model = ExtensionToolbarModel::Get(profile);
+ToolbarActionsModel* CreateToolbarModelImpl(Profile* profile,
+ bool wait_for_ready) {
+ ToolbarActionsModel* model = ToolbarActionsModel::Get(profile);
if (model)
return model;
// No existing model means it's a new profile (since we, by default, don't
// create the ToolbarModel in testing).
- ExtensionToolbarModelFactory::GetInstance()->SetTestingFactory(
+ ToolbarActionsModelFactory::GetInstance()->SetTestingFactory(
profile, &BuildToolbarModel);
- model = ExtensionToolbarModel::Get(profile);
+ model = ToolbarActionsModel::Get(profile);
if (wait_for_ready) {
// Fake the extension system ready signal.
// HACK ALERT! In production, the ready task on ExtensionSystem (and most
@@ -151,11 +158,11 @@ scoped_refptr<const Extension> CreateActionExtension(
Build();
}
-ExtensionToolbarModel* CreateToolbarModelForProfile(Profile* profile) {
+ToolbarActionsModel* CreateToolbarModelForProfile(Profile* profile) {
return CreateToolbarModelImpl(profile, true);
}
-ExtensionToolbarModel* CreateToolbarModelForProfileWithoutWaitingForReady(
+ToolbarActionsModel* CreateToolbarModelForProfileWithoutWaitingForReady(
Profile* profile) {
return CreateToolbarModelImpl(profile, false);
}
diff --git a/chrome/browser/extensions/extension_action_test_util.h b/chrome/browser/extensions/extension_action_test_util.h
index 0d52726..7f5a7e2 100644
--- a/chrome/browser/extensions/extension_action_test_util.h
+++ b/chrome/browser/extensions/extension_action_test_util.h
@@ -12,6 +12,7 @@
#include "extensions/common/manifest.h"
class Profile;
+class ToolbarActionsModel;
namespace content {
class WebContents;
@@ -19,7 +20,6 @@ class WebContents;
namespace extensions {
class Extension;
-class ExtensionToolbarModel;
namespace extension_action_test_util {
@@ -51,15 +51,15 @@ scoped_refptr<const Extension> CreateActionExtension(
ActionType action_type,
Manifest::Location location);
-// Creates a new ExtensionToolbarModel for the given |profile|, and associates
+// Creates a new ToolbarActionsModel for the given |profile|, and associates
// it with the profile as a keyed service.
// This should only be used in unit tests (since it assumes the existence of
// a TestExtensionSystem), but if running a browser test, the model should
// already be created.
-ExtensionToolbarModel* CreateToolbarModelForProfile(Profile* profile);
+ToolbarActionsModel* CreateToolbarModelForProfile(Profile* profile);
// Like above, but doesn't run the ExtensionSystem::ready() task for the new
// model.
-ExtensionToolbarModel* CreateToolbarModelForProfileWithoutWaitingForReady(
+ToolbarActionsModel* CreateToolbarModelForProfileWithoutWaitingForReady(
Profile* profile);
} // namespace extension_action_test_util
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.cc b/chrome/browser/extensions/extension_message_bubble_controller.cc
index 9c2430a..3fe6dc5 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller.cc
@@ -10,10 +10,10 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_message_bubble.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/user_metrics.h"
#include "extensions/browser/extension_prefs.h"
@@ -162,8 +162,8 @@ void ExtensionMessageBubbleController::HighlightExtensionsIfNecessary() {
did_highlight_ = true;
const ExtensionIdList& extension_ids = GetExtensionIdList();
DCHECK(!extension_ids.empty());
- ExtensionToolbarModel::Get(profile())->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
+ ToolbarActionsModel::Get(profile())->HighlightActions(
+ extension_ids, ToolbarActionsModel::HIGHLIGHT_WARNING);
}
}
@@ -248,7 +248,7 @@ ExtensionIdList* ExtensionMessageBubbleController::GetOrCreateExtensionList() {
void ExtensionMessageBubbleController::OnClose() {
AcknowledgeExtensions();
if (did_highlight_)
- ExtensionToolbarModel::Get(profile())->StopHighlighting();
+ ToolbarActionsModel::Get(profile())->StopHighlighting();
}
} // namespace extensions
diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc
deleted file mode 100644
index bad9027..0000000
--- a/chrome/browser/extensions/extension_toolbar_model.cc
+++ /dev/null
@@ -1,753 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/extension_toolbar_model.h"
-
-#include <algorithm>
-#include <string>
-
-#include "base/location.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/histogram_base.h"
-#include "base/prefs/pref_service.h"
-#include "base/single_thread_task_runner.h"
-#include "base/thread_task_runner_handle.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/tab_helper.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/pref_names.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "extensions/common/feature_switch.h"
-#include "extensions/common/manifest_constants.h"
-#include "extensions/common/one_shot_event.h"
-
-namespace extensions {
-
-ExtensionToolbarModel::ExtensionToolbarModel(Profile* profile,
- ExtensionPrefs* extension_prefs)
- : profile_(profile),
- extension_prefs_(extension_prefs),
- prefs_(profile_->GetPrefs()),
- extension_action_api_(ExtensionActionAPI::Get(profile_)),
- extensions_initialized_(false),
- include_all_extensions_(FeatureSwitch::extension_action_redesign()
- ->IsEnabled()),
- highlight_type_(HIGHLIGHT_NONE),
- extension_action_observer_(this),
- extension_registry_observer_(this),
- weak_ptr_factory_(this) {
- ExtensionSystem::Get(profile_)->ready().Post(
- FROM_HERE,
- base::Bind(&ExtensionToolbarModel::OnReady,
- weak_ptr_factory_.GetWeakPtr()));
- visible_icon_count_ = prefs_->GetInteger(pref_names::kToolbarSize);
-
- // We only care about watching the prefs if not in incognito mode.
- if (!profile_->IsOffTheRecord()) {
- pref_change_registrar_.Init(prefs_);
- pref_change_callback_ =
- base::Bind(&ExtensionToolbarModel::OnExtensionToolbarPrefChange,
- base::Unretained(this));
- pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
- }
-}
-
-ExtensionToolbarModel::~ExtensionToolbarModel() {
-}
-
-// static
-ExtensionToolbarModel* ExtensionToolbarModel::Get(Profile* profile) {
- return ExtensionToolbarModelFactory::GetForProfile(profile);
-}
-
-void ExtensionToolbarModel::AddObserver(Observer* observer) {
- observers_.AddObserver(observer);
-}
-
-void ExtensionToolbarModel::RemoveObserver(Observer* observer) {
- observers_.RemoveObserver(observer);
-}
-
-void ExtensionToolbarModel::MoveExtensionIcon(const std::string& id,
- size_t index) {
- ExtensionList::iterator pos = toolbar_items_.begin();
- while (pos != toolbar_items_.end() && (*pos)->id() != id)
- ++pos;
- if (pos == toolbar_items_.end()) {
- NOTREACHED();
- return;
- }
- scoped_refptr<const Extension> extension = *pos;
- toolbar_items_.erase(pos);
-
- ExtensionIdList::iterator pos_id = std::find(last_known_positions_.begin(),
- last_known_positions_.end(),
- id);
- if (pos_id != last_known_positions_.end())
- last_known_positions_.erase(pos_id);
-
- if (index < toolbar_items_.size()) {
- // If the index is not at the end, find the item currently at |index|, and
- // insert |extension| before it in both |toolbar_items_| and
- // |last_known_positions_|.
- ExtensionList::iterator iter = toolbar_items_.begin() + index;
- last_known_positions_.insert(std::find(last_known_positions_.begin(),
- last_known_positions_.end(),
- (*iter)->id()),
- id);
- toolbar_items_.insert(iter, extension);
- } else {
- // Otherwise, put |extension| at the end.
- DCHECK_EQ(toolbar_items_.size(), index);
- index = toolbar_items_.size();
- toolbar_items_.push_back(extension);
- last_known_positions_.push_back(id);
- }
-
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarExtensionMoved(extension.get(), index));
- MaybeUpdateVisibilityPref(extension.get(), index);
- UpdatePrefs();
-}
-
-void ExtensionToolbarModel::SetVisibleIconCount(size_t count) {
- visible_icon_count_ = (count >= toolbar_items_.size()) ? -1 : count;
-
- // Only set the prefs if we're not in highlight mode and the profile is not
- // incognito. Highlight mode is designed to be a transitory state, and should
- // not persist across browser restarts (though it may be re-entered), and we
- // don't store anything in incognito.
- if (!is_highlighting() && !profile_->IsOffTheRecord()) {
- // Additionally, if we are using the new toolbar, any icons which are in the
- // overflow menu are considered "hidden". But it so happens that the times
- // we are likely to call SetVisibleIconCount() are also those when we are
- // in flux. So wait for things to cool down before setting the prefs.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&ExtensionToolbarModel::MaybeUpdateVisibilityPrefs,
- weak_ptr_factory_.GetWeakPtr()));
- prefs_->SetInteger(pref_names::kToolbarSize, visible_icon_count_);
- }
-
- FOR_EACH_OBSERVER(Observer, observers_, OnToolbarVisibleCountChanged());
-}
-
-void ExtensionToolbarModel::OnExtensionActionUpdated(
- ExtensionAction* extension_action,
- content::WebContents* web_contents,
- content::BrowserContext* browser_context) {
- const Extension* extension =
- ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
- extension_action->extension_id());
- // Notify observers if the extension exists and is in the model.
- if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) !=
- toolbar_items_.end()) {
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarExtensionUpdated(extension));
- }
-}
-
-void ExtensionToolbarModel::OnExtensionActionVisibilityChanged(
- const std::string& extension_id,
- bool is_now_visible) {
- const Extension* extension =
- ExtensionRegistry::Get(profile_)->GetExtensionById(
- extension_id, ExtensionRegistry::EVERYTHING);
-
- // Hiding works differently with the new and old toolbars.
- if (include_all_extensions_) {
- // It's possible that we haven't added this extension yet, if its
- // visibility was adjusted in the course of its initialization.
- if (std::find(toolbar_items_.begin(), toolbar_items_.end(), extension) ==
- toolbar_items_.end())
- return;
-
- int new_size = 0;
- int new_index = 0;
- if (is_now_visible) {
- // If this action used to be hidden, we can't possibly be showing all.
- DCHECK_LT(visible_icon_count(), toolbar_items_.size());
- // Grow the bar by one and move the extension to the end of the visibles.
- new_size = visible_icon_count() + 1;
- new_index = new_size - 1;
- } else {
- // If we're hiding one, we must be showing at least one.
- DCHECK_GE(visible_icon_count(), 0u);
- // Shrink the bar by one and move the extension to the beginning of the
- // overflow menu.
- new_size = visible_icon_count() - 1;
- new_index = new_size;
- }
- SetVisibleIconCount(new_size);
- MoveExtensionIcon(extension->id(), new_index);
- } else { // Don't include all extensions.
- if (is_now_visible)
- AddExtension(extension);
- else
- RemoveExtension(extension);
- }
-}
-
-void ExtensionToolbarModel::OnExtensionLoaded(
- content::BrowserContext* browser_context,
- const Extension* extension) {
- // We don't want to add the same extension twice. It may have already been
- // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
- // hides the browser action and then disables and enables the extension.
- for (size_t i = 0; i < toolbar_items_.size(); i++) {
- if (toolbar_items_[i].get() == extension)
- return;
- }
-
- AddExtension(extension);
-}
-
-void ExtensionToolbarModel::OnExtensionUnloaded(
- content::BrowserContext* browser_context,
- const Extension* extension,
- UnloadedExtensionInfo::Reason reason) {
- RemoveExtension(extension);
-}
-
-void ExtensionToolbarModel::OnExtensionUninstalled(
- content::BrowserContext* browser_context,
- const Extension* extension,
- extensions::UninstallReason reason) {
- // Remove the extension id from the ordered list, if it exists (the extension
- // might not be represented in the list because it might not have an icon).
- ExtensionIdList::iterator pos =
- std::find(last_known_positions_.begin(),
- last_known_positions_.end(), extension->id());
-
- if (pos != last_known_positions_.end()) {
- last_known_positions_.erase(pos);
- UpdatePrefs();
- }
-}
-
-void ExtensionToolbarModel::OnReady() {
- ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
- InitializeExtensionList();
- // Wait until the extension system is ready before observing any further
- // changes so that the toolbar buttons can be shown in their stable ordering
- // taken from prefs.
- extension_registry_observer_.Add(registry);
- extension_action_observer_.Add(extension_action_api_);
-
- if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
- profile_)) {
- ExtensionIdList ids;
- for (const auto& extension : toolbar_items_)
- ids.push_back(extension->id());
- HighlightExtensions(ids, HIGHLIGHT_INFO);
- }
-
- extensions_initialized_ = true;
- FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
-}
-
-size_t ExtensionToolbarModel::FindNewPositionFromLastKnownGood(
- const Extension* extension) {
- // See if we have last known good position for this extension.
- size_t new_index = 0;
- // Loop through the ID list of known positions, to count the number of visible
- // extension icons preceding |extension|.
- for (ExtensionIdList::const_iterator iter_id = last_known_positions_.begin();
- iter_id < last_known_positions_.end(); ++iter_id) {
- if ((*iter_id) == extension->id())
- return new_index; // We've found the right position.
- // Found an id, need to see if it is visible.
- for (ExtensionList::const_iterator iter_ext = toolbar_items_.begin();
- iter_ext < toolbar_items_.end(); ++iter_ext) {
- if ((*iter_ext)->id() == (*iter_id)) {
- // This extension is visible, update the index value.
- ++new_index;
- break;
- }
- }
- }
-
- // Position not found.
- return toolbar_items_.size();
-}
-
-bool ExtensionToolbarModel::ShouldAddExtension(const Extension* extension) {
- // In incognito mode, don't add any extensions that aren't incognito-enabled.
- if (profile_->IsOffTheRecord() &&
- !util::IsIncognitoEnabled(extension->id(), profile_))
- return false;
-
- ExtensionActionManager* action_manager =
- ExtensionActionManager::Get(profile_);
- if (include_all_extensions_) {
- // In this case, we don't care about the browser action visibility, because
- // we want to show each extension regardless.
- // TODO(devlin): Extension actions which are not visible should be moved to
- // the overflow menu by default.
- return action_manager->GetExtensionAction(*extension) != NULL;
- }
-
- return action_manager->GetBrowserAction(*extension) &&
- extension_action_api_->GetBrowserActionVisibility(extension->id());
-}
-
-void ExtensionToolbarModel::AddExtension(const Extension* extension) {
- // We only use AddExtension() once the system is initialized.
- DCHECK(extensions_initialized_);
- if (!ShouldAddExtension(extension))
- return;
-
- // See if we have a last known good position for this extension.
- bool is_new_extension =
- std::find(last_known_positions_.begin(),
- last_known_positions_.end(),
- extension->id()) == last_known_positions_.end();
-
- // New extensions go at the right (end) of the visible extensions. Other
- // extensions go at their previous position.
- size_t new_index = 0;
- if (is_new_extension) {
- new_index = Manifest::IsComponentLocation(extension->location()) ?
- 0 : visible_icon_count();
- // For the last-known position, we use the index of the extension that is
- // just before this extension, plus one. (Note that this isn't the same
- // as new_index + 1, because last_known_positions_ can include disabled
- // extensions.)
- int new_last_known_index =
- new_index == 0 ? 0 :
- std::find(last_known_positions_.begin(),
- last_known_positions_.end(),
- toolbar_items_[new_index - 1]->id()) -
- last_known_positions_.begin() + 1;
- // In theory, the extension before this one should always
- // be in last known positions, but if something funny happened with prefs,
- // make sure we handle it.
- // TODO(devlin): Track down these cases so we can CHECK this.
- new_last_known_index =
- std::min<int>(new_last_known_index, last_known_positions_.size());
- last_known_positions_.insert(
- last_known_positions_.begin() + new_last_known_index, extension->id());
- UpdatePrefs();
- } else {
- new_index = FindNewPositionFromLastKnownGood(extension);
- }
-
- toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
-
- // If we're currently highlighting, then even though we add a browser action
- // to the full list (|toolbar_items_|, there won't be another *visible*
- // browser action, which was what the observers care about.
- if (!is_highlighting()) {
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarExtensionAdded(extension, new_index));
-
- int visible_count_delta = 0;
- if (is_new_extension && !all_icons_visible()) {
- // If this is a new extension (and not all extensions are visible), we
- // expand the toolbar out so that the new one can be seen.
- visible_count_delta = 1;
- } else if (profile_->IsOffTheRecord()) {
- // If this is an incognito profile, we also have to check to make sure the
- // overflow matches the main bar's status.
- ExtensionToolbarModel* main_model =
- ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
- // Find what the index will be in the main bar. Because Observer calls are
- // nondeterministic, we can't just assume the main bar will have the
- // extension and look it up.
- size_t main_index =
- main_model->FindNewPositionFromLastKnownGood(extension);
- bool visible = main_index < main_model->visible_icon_count();
- // We may need to adjust the visible count if the incognito bar isn't
- // showing all icons and this one is visible, or if it is showing all
- // icons and this is hidden.
- if (visible && !all_icons_visible())
- visible_count_delta = 1;
- else if (!visible && all_icons_visible())
- visible_count_delta = -1;
- }
-
- if (visible_count_delta)
- SetVisibleIconCount(visible_icon_count() + visible_count_delta);
- }
-
- MaybeUpdateVisibilityPref(extension, new_index);
-}
-
-void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
- ExtensionList::iterator pos =
- std::find(toolbar_items_.begin(), toolbar_items_.end(), extension);
- if (pos == toolbar_items_.end())
- return;
-
- size_t index = pos - toolbar_items_.begin();
- // If the removed extension was on the toolbar, a new one will take its place
- // if there are any in overflow.
- bool new_extension_shown =
- !all_icons_visible() && index < visible_icon_count();
-
- // If our visible count is set to the current size, we need to decrement it.
- if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
- SetVisibleIconCount(toolbar_items_.size() - 1);
-
- toolbar_items_.erase(pos);
-
- // If we're in highlight mode, we also have to remove the extension from
- // the highlighted list.
- if (is_highlighting()) {
- pos = std::find(highlighted_items_.begin(),
- highlighted_items_.end(),
- extension);
- if (pos != highlighted_items_.end()) {
- highlighted_items_.erase(pos);
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarExtensionRemoved(extension));
- // If the highlighted list is now empty, we stop highlighting.
- if (highlighted_items_.empty())
- StopHighlighting();
- }
- } else {
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarExtensionRemoved(extension));
- }
-
- UpdatePrefs();
- if (new_extension_shown) {
- size_t newly_visible_index = visible_icon_count() - 1;
- MaybeUpdateVisibilityPref(toolbar_items_[newly_visible_index].get(),
- newly_visible_index);
- }
-}
-
-// Combine the currently enabled extensions that have browser actions (which
-// we get from the ExtensionRegistry) with the ordering we get from the
-// pref service. For robustness we use a somewhat inefficient process:
-// 1. Create a vector of extensions sorted by their pref values. This vector may
-// have holes.
-// 2. Create a vector of extensions that did not have a pref value.
-// 3. Remove holes from the sorted vector and append the unsorted vector.
-void ExtensionToolbarModel::InitializeExtensionList() {
- DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
-
- last_known_positions_ = extension_prefs_->GetToolbarOrder();
- if (profile_->IsOffTheRecord())
- IncognitoPopulate();
- else
- Populate(&last_known_positions_);
-
- MaybeUpdateVisibilityPrefs();
-}
-
-void ExtensionToolbarModel::Populate(ExtensionIdList* positions) {
- DCHECK(!profile_->IsOffTheRecord());
- const ExtensionSet& extensions =
- ExtensionRegistry::Get(profile_)->enabled_extensions();
- // Items that have explicit positions.
- ExtensionList sorted(positions->size(), NULL);
- // The items that don't have explicit positions.
- ExtensionList unsorted;
-
- // Create the lists.
- int hidden = 0;
- for (const scoped_refptr<const Extension>& extension : extensions) {
- if (!ShouldAddExtension(extension.get())) {
- if (!extension_action_api_->GetBrowserActionVisibility(extension->id()))
- ++hidden;
- continue;
- }
-
- ExtensionIdList::const_iterator pos =
- std::find(positions->begin(), positions->end(), extension->id());
- if (pos != positions->end()) {
- sorted[pos - positions->begin()] = extension;
- } else {
- // Unknown extension - push it to the back of unsorted, and add it to the
- // list of ids at the end.
- unsorted.push_back(extension);
- positions->push_back(extension->id());
- }
- }
-
- // Merge the lists.
- sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
- toolbar_items_.reserve(sorted.size());
-
- for (const scoped_refptr<const Extension>& extension : sorted) {
- // It's possible for the extension order to contain items that aren't
- // actually loaded on this machine. For example, when extension sync is on,
- // we sync the extension order as-is but double-check with the user before
- // syncing NPAPI-containing extensions, so if one of those is not actually
- // synced, we'll get a NULL in the list. This sort of case can also happen
- // if some error prevents an extension from loading.
- if (extension.get()) {
- // We don't notify observers of the added extension yet. Rather, observers
- // should wait for the "OnToolbarModelInitialized" notification, and then
- // bulk-update. (This saves a lot of bouncing-back-and-forth here, and
- // allows observers to ensure that the extension system is always
- // initialized before using the extensions).
- toolbar_items_.push_back(extension);
- }
- }
-
- UMA_HISTOGRAM_COUNTS_100(
- "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
- UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
- toolbar_items_.size());
-
- if (!toolbar_items_.empty()) {
- // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
- // values to 0, this would be counted as 'show none' unless we convert it to
- // max.
- UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsVisible",
- visible_icon_count_ == -1 ?
- base::HistogramBase::kSampleType_MAX :
- visible_icon_count_);
- }
-}
-
-void ExtensionToolbarModel::IncognitoPopulate() {
- DCHECK(profile_->IsOffTheRecord());
- const ExtensionToolbarModel* original_model =
- ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
-
- // Find the absolute value of the original model's count.
- int original_visible = original_model->visible_icon_count();
-
- // In incognito mode, we show only those extensions that are
- // incognito-enabled. Further, any actions that were overflowed in regular
- // mode are still overflowed. Order is the same as in regular mode.
- visible_icon_count_ = 0;
- for (ExtensionList::const_iterator iter =
- original_model->toolbar_items_.begin();
- iter != original_model->toolbar_items_.end(); ++iter) {
- if (ShouldAddExtension(iter->get())) {
- toolbar_items_.push_back(*iter);
- if (iter - original_model->toolbar_items_.begin() < original_visible)
- ++visible_icon_count_;
- }
- }
-}
-
-void ExtensionToolbarModel::UpdatePrefs() {
- if (!extension_prefs_ || profile_->IsOffTheRecord())
- return;
-
- // Don't observe change caused by self.
- pref_change_registrar_.Remove(pref_names::kToolbar);
- extension_prefs_->SetToolbarOrder(last_known_positions_);
- pref_change_registrar_.Add(pref_names::kToolbar, pref_change_callback_);
-}
-
-void ExtensionToolbarModel::MaybeUpdateVisibilityPref(
- const Extension* extension, size_t index) {
- // We only update the visibility pref for hidden/not hidden based on the
- // overflow menu with the new toolbar design.
- if (include_all_extensions_ && !profile_->IsOffTheRecord()) {
- bool visible = index < visible_icon_count();
- if (visible != extension_action_api_->GetBrowserActionVisibility(
- extension->id())) {
- // Don't observe changes caused by ourselves.
- bool was_registered = false;
- if (extension_action_observer_.IsObserving(extension_action_api_)) {
- was_registered = true;
- extension_action_observer_.RemoveAll();
- }
- extension_action_api_->SetBrowserActionVisibility(extension->id(),
- visible);
- if (was_registered)
- extension_action_observer_.Add(extension_action_api_);
- }
- }
-}
-
-void ExtensionToolbarModel::MaybeUpdateVisibilityPrefs() {
- for (size_t i = 0u; i < toolbar_items_.size(); ++i)
- MaybeUpdateVisibilityPref(toolbar_items_[i].get(), i);
-}
-
-void ExtensionToolbarModel::OnExtensionToolbarPrefChange() {
- // If extensions are not ready, defer to later Populate() call.
- if (!extensions_initialized_)
- return;
-
- // Recalculate |last_known_positions_| to be |pref_positions| followed by
- // ones that are only in |last_known_positions_|.
- ExtensionIdList pref_positions = extension_prefs_->GetToolbarOrder();
- size_t pref_position_size = pref_positions.size();
- for (size_t i = 0; i < last_known_positions_.size(); ++i) {
- if (std::find(pref_positions.begin(), pref_positions.end(),
- last_known_positions_[i]) == pref_positions.end()) {
- pref_positions.push_back(last_known_positions_[i]);
- }
- }
- last_known_positions_.swap(pref_positions);
-
- int desired_index = 0;
- // Loop over the updated list of last known positions, moving any extensions
- // that are in the wrong place.
- for (const std::string& id : last_known_positions_) {
- int current_index = GetIndexForId(id);
- if (current_index == -1)
- continue;
- if (current_index != desired_index) {
- scoped_refptr<const Extension> extension = toolbar_items_[current_index];
- toolbar_items_.erase(toolbar_items_.begin() + current_index);
- toolbar_items_.insert(toolbar_items_.begin() + desired_index, extension);
- // Notify the observers to keep them up-to-date.
- FOR_EACH_OBSERVER(
- Observer, observers_,
- OnToolbarExtensionMoved(extension.get(), desired_index));
- }
- ++desired_index;
- }
-
- if (last_known_positions_.size() > pref_position_size) {
- // Need to update pref because we have extra icons. But can't call
- // UpdatePrefs() directly within observation closure.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::Bind(&ExtensionToolbarModel::UpdatePrefs,
- weak_ptr_factory_.GetWeakPtr()));
- }
-}
-
-int ExtensionToolbarModel::GetIndexForId(const std::string& id) const {
- for (size_t i = 0; i < toolbar_items().size(); ++i) {
- if (toolbar_items()[i]->id() == id)
- return i;
- }
- return -1;
-}
-
-bool ExtensionToolbarModel::ShowExtensionActionPopup(
- const Extension* extension,
- Browser* browser,
- bool grant_active_tab) {
- base::ObserverListBase<Observer>::Iterator it(&observers_);
- Observer* obs = NULL;
- // Look for the Observer associated with the browser.
- // This would be cleaner if we had an abstract class for the Toolbar UI
- // (like we do for LocationBar), but sadly, we don't.
- while ((obs = it.GetNext()) != NULL) {
- if (obs->GetBrowser() == browser)
- return obs->ShowExtensionActionPopup(extension, grant_active_tab);
- }
- return false;
-}
-
-void ExtensionToolbarModel::EnsureVisibility(
- const ExtensionIdList& extension_ids) {
- if (all_icons_visible())
- return; // Already showing all.
-
- // Otherwise, make sure we have enough room to show all the extensions
- // requested.
- if (visible_icon_count() < extension_ids.size())
- SetVisibleIconCount(extension_ids.size());
-
- if (all_icons_visible())
- return; // May have been set to max by SetVisibleIconCount.
-
- // Guillotine's Delight: Move an orange noble to the front of the line.
- for (ExtensionIdList::const_iterator it = extension_ids.begin();
- it != extension_ids.end(); ++it) {
- for (ExtensionList::const_iterator extension = toolbar_items_.begin();
- extension != toolbar_items_.end(); ++extension) {
- if ((*extension)->id() == (*it)) {
- if (extension - toolbar_items_.begin() >=
- static_cast<int>(visible_icon_count()))
- MoveExtensionIcon((*extension)->id(), 0);
- break;
- }
- }
- }
-}
-
-bool ExtensionToolbarModel::HighlightExtensions(
- const ExtensionIdList& extension_ids,
- HighlightType highlight_type) {
- highlighted_items_.clear();
-
- for (ExtensionIdList::const_iterator id = extension_ids.begin();
- id != extension_ids.end();
- ++id) {
- for (ExtensionList::const_iterator extension = toolbar_items_.begin();
- extension != toolbar_items_.end();
- ++extension) {
- if (*id == (*extension)->id())
- highlighted_items_.push_back(*extension);
- }
- }
-
- // If we have any items in |highlighted_items_|, then we entered highlighting
- // mode.
- if (highlighted_items_.size()) {
- // It's important that is_highlighting_ is changed immediately before the
- // observers are notified since it changes the result of toolbar_items().
- highlight_type_ = highlight_type;
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarHighlightModeChanged(true));
-
- // We set the visible icon count after the highlight mode change because
- // the UI actions are created/destroyed during highlight, and doing that
- // prior to changing the size allows us to still have smooth animations.
- if (visible_icon_count() < extension_ids.size())
- SetVisibleIconCount(extension_ids.size());
-
- return true;
- }
-
- // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
- // we were otherwise in it).
- if (is_highlighting())
- StopHighlighting();
- return false;
-}
-
-void ExtensionToolbarModel::StopHighlighting() {
- if (is_highlighting()) {
- // It's important that is_highlighting_ is changed immediately before the
- // observers are notified since it changes the result of toolbar_items().
- highlight_type_ = HIGHLIGHT_NONE;
- FOR_EACH_OBSERVER(Observer, observers_,
- OnToolbarHighlightModeChanged(false));
-
- // For the same reason, we don't clear highlighted_items_ until after the
- // mode changed.
- highlighted_items_.clear();
-
- // We set the visible icon count after the highlight mode change because
- // the UI actions are created/destroyed during highlight, and doing that
- // prior to changing the size allows us to still have smooth animations.
- int saved_icon_count = prefs_->GetInteger(pref_names::kToolbarSize);
- if (saved_icon_count != visible_icon_count_)
- SetVisibleIconCount(saved_icon_count);
- }
-}
-
-bool ExtensionToolbarModel::RedesignIsShowingNewIcons() const {
- for (const scoped_refptr<const Extension>& extension : toolbar_items_) {
- // Without the redesign, we only show extensions with browser actions.
- // Any extension without a browser action is an indication that we're
- // showing something new.
- if (!extension->manifest()->HasKey(manifest_keys::kBrowserAction))
- return true;
- }
- return false;
-}
-
-} // namespace extensions
diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h
deleted file mode 100644
index 2093ac8..0000000
--- a/chrome/browser/extensions/extension_toolbar_model.h
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_H_
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_H_
-
-#include "base/compiler_specific.h"
-#include "base/observer_list.h"
-#include "base/prefs/pref_change_registrar.h"
-#include "base/scoped_observer.h"
-#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_action.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry_observer.h"
-#include "extensions/common/extension.h"
-
-class Browser;
-class PrefService;
-class Profile;
-
-namespace extensions {
-class ExtensionRegistry;
-class ExtensionSet;
-
-// Model for the browser actions toolbar.
-class ExtensionToolbarModel : public ExtensionActionAPI::Observer,
- public ExtensionRegistryObserver,
- public KeyedService {
- public:
- // The different options for highlighting.
- enum HighlightType {
- HIGHLIGHT_NONE,
- HIGHLIGHT_INFO,
- HIGHLIGHT_WARNING,
- };
-
- ExtensionToolbarModel(Profile* profile, ExtensionPrefs* extension_prefs);
- ~ExtensionToolbarModel() override;
-
- // A class which is informed of changes to the model; represents the view of
- // MVC. Also used for signaling view changes such as showing extension popups.
- // TODO(devlin): Should this really be an observer? It acts more like a
- // delegate.
- class Observer {
- public:
- // Signals that an |extension| has been added to the toolbar at |index|.
- // This will *only* be called after the toolbar model has been initialized.
- virtual void OnToolbarExtensionAdded(const Extension* extension,
- int index) = 0;
-
- // Signals that the given |extension| has been removed from the toolbar.
- virtual void OnToolbarExtensionRemoved(const Extension* extension) = 0;
-
- // Signals that the given |extension| has been moved to |index|. |index| is
- // the desired *final* index of the extension (that is, in the adjusted
- // order, extension should be at |index|).
- virtual void OnToolbarExtensionMoved(const Extension* extension,
- int index) = 0;
-
- // Signals that the browser action for the given |extension| has been
- // updated.
- virtual void OnToolbarExtensionUpdated(const Extension* extension) = 0;
-
- // Signals the |extension| to show the popup now in the active window.
- // If |grant_active_tab| is true, then active tab permissions should be
- // given to the extension (only do this if this is through a user action).
- // Returns true if a popup was slated to be shown.
- virtual bool ShowExtensionActionPopup(const Extension* extension,
- bool grant_active_tab) = 0;
-
- // Signals when the container needs to be redrawn because of a size change,
- // and when the model has finished loading.
- virtual void OnToolbarVisibleCountChanged() = 0;
-
- // Signals that the model has entered or exited highlighting mode, or that
- // the extensions being highlighted have (probably*) changed. Highlighting
- // mode indicates that only a subset of the extensions are actively
- // displayed, and those extensions should be highlighted for extra emphasis.
- // * probably, because if we are in highlight mode and receive a call to
- // highlight a new set of extensions, we do not compare the current set
- // with the new set (and just assume the new set is different).
- virtual void OnToolbarHighlightModeChanged(bool is_highlighting) = 0;
-
- // Signals that the toolbar model has been initialized, so that if any
- // observers were postponing animation during the initialization stage, they
- // can catch up.
- virtual void OnToolbarModelInitialized() = 0;
-
- // Returns the browser associated with the Observer.
- virtual Browser* GetBrowser() = 0;
-
- protected:
- virtual ~Observer() {}
- };
-
- // Convenience function to get the ExtensionToolbarModel for a Profile.
- static ExtensionToolbarModel* Get(Profile* profile);
-
- // Adds or removes an observer.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
- // Moves the given |extension|'s icon to the given |index|.
- void MoveExtensionIcon(const std::string& id, size_t index);
-
- // Sets the number of extension icons that should be visible.
- // If count == size(), this will set the visible icon count to -1, meaning
- // "show all actions".
- void SetVisibleIconCount(size_t count);
-
- size_t visible_icon_count() const {
- // We have guards around this because |visible_icon_count_| can be set by
- // prefs/sync, and we want to ensure that the icon count returned is within
- // bounds.
- return visible_icon_count_ == -1 ?
- toolbar_items().size() :
- std::min(static_cast<size_t>(visible_icon_count_),
- toolbar_items().size());
- }
-
- bool all_icons_visible() const { return visible_icon_count_ == -1; }
-
- bool extensions_initialized() const { return extensions_initialized_; }
-
- const ExtensionList& toolbar_items() const {
- return is_highlighting() ? highlighted_items_ : toolbar_items_;
- }
-
- bool is_highlighting() const { return highlight_type_ != HIGHLIGHT_NONE; }
- HighlightType highlight_type() const { return highlight_type_; }
-
- void OnExtensionToolbarPrefChange();
-
- // Returns the index of the given |id|, or -1 if the id wasn't found.
- int GetIndexForId(const std::string& id) const;
-
- // Finds the Observer associated with |browser| and tells it to display a
- // popup for the given |extension|. If |grant_active_tab| is true, this
- // grants active tab permissions to the |extension|; only do this because of
- // a direct user action.
- bool ShowExtensionActionPopup(const Extension* extension,
- Browser* browser,
- bool grant_active_tab);
-
- // Ensures that the extensions in the |extension_ids| list are visible on the
- // toolbar. This might mean they need to be moved to the front (if they are in
- // the overflow bucket).
- void EnsureVisibility(const ExtensionIdList& extension_ids);
-
- // Highlights the extensions specified by |extension_ids|. This will cause
- // the ToolbarModel to only display those extensions.
- // Highlighting mode is only entered if there is at least one extension to
- // be shown.
- // Returns true if highlighting mode is entered, false otherwise.
- bool HighlightExtensions(const ExtensionIdList& extension_ids,
- HighlightType type);
-
- // Stop highlighting extensions. All extensions can be shown again, and the
- // number of visible icons will be reset to what it was before highlighting.
- void StopHighlighting();
-
- // Returns true if the toolbar model is running with the redesign and is
- // showing new icons as a result.
- bool RedesignIsShowingNewIcons() const;
-
- private:
- // Callback when extensions are ready.
- void OnReady();
-
- // ExtensionRegistryObserver:
- void OnExtensionLoaded(content::BrowserContext* browser_context,
- const Extension* extension) override;
- void OnExtensionUnloaded(content::BrowserContext* browser_context,
- const Extension* extension,
- UnloadedExtensionInfo::Reason reason) override;
- void OnExtensionUninstalled(content::BrowserContext* browser_context,
- const Extension* extension,
- extensions::UninstallReason reason) override;
-
- // ExtensionActionAPI::Observer:
- void OnExtensionActionUpdated(
- ExtensionAction* extension_action,
- content::WebContents* web_contents,
- content::BrowserContext* browser_context) override;
- void OnExtensionActionVisibilityChanged(const std::string& extension_id,
- bool is_now_visible) override;
-
- // To be called after the extension service is ready; gets loaded extensions
- // from the ExtensionRegistry and their saved order from the pref service
- // and constructs |toolbar_items_| from these data. IncognitoPopulate()
- // takes the shortcut - looking at the regular model's content and modifying
- // it.
- void InitializeExtensionList();
- void Populate(ExtensionIdList* positions);
- void IncognitoPopulate();
-
- // Save the model to prefs.
- void UpdatePrefs();
-
- // Updates |extension|'s browser action visibility pref if the browser action
- // is in the overflow menu and should be considered hidden.
- void MaybeUpdateVisibilityPref(const Extension* extension, size_t index);
-
- // Calls MaybeUpdateVisibilityPref() for each extension in |toolbar_items|.
- void MaybeUpdateVisibilityPrefs();
-
- // Finds the last known visible position of the icon for an |extension|. The
- // value returned is a zero-based index into the vector of visible items.
- size_t FindNewPositionFromLastKnownGood(const Extension* extension);
-
- // Returns true if the given |extension| should be added to the toolbar.
- bool ShouldAddExtension(const Extension* extension);
-
- // Adds or removes the given |extension| from the toolbar model.
- void AddExtension(const Extension* extension);
- void RemoveExtension(const Extension* extension);
-
- // Our observers.
- base::ObserverList<Observer> observers_;
-
- // The Profile this toolbar model is for.
- Profile* profile_;
-
- ExtensionPrefs* extension_prefs_;
- PrefService* prefs_;
-
- // The ExtensionActionAPI object, cached for convenience.
- ExtensionActionAPI* extension_action_api_;
-
- // True if we've handled the initial EXTENSIONS_READY notification.
- bool extensions_initialized_;
-
- // If true, we include all extensions in the toolbar model. If false, we only
- // include browser actions.
- bool include_all_extensions_;
-
- // Ordered list of browser action buttons.
- ExtensionList toolbar_items_;
-
- // List of browser action buttons which should be highlighted.
- ExtensionList highlighted_items_;
-
- // The current type of highlight (with HIGHLIGHT_NONE indicating no current
- // highlight).
- HighlightType highlight_type_;
-
- ExtensionIdList last_known_positions_;
-
- // The number of icons visible (the rest should be hidden in the overflow
- // chevron). A value of -1 indicates that all icons should be visible.
- // Instead of using this variable directly, use visible_icon_count() if
- // possible.
- // TODO(devlin): Make a new variable to indicate that all icons should be
- // visible, instead of overloading this one.
- int visible_icon_count_;
-
- ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer>
- extension_action_observer_;
-
- // Listen to extension load, unloaded notifications.
- ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
- extension_registry_observer_;
-
- // For observing change of toolbar order preference by external entity (sync).
- PrefChangeRegistrar pref_change_registrar_;
- base::Closure pref_change_callback_;
-
- base::WeakPtrFactory<ExtensionToolbarModel> weak_ptr_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarModel);
-};
-
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_H_
diff --git a/chrome/browser/extensions/extension_toolbar_model_factory.cc b/chrome/browser/extensions/extension_toolbar_model_factory.cc
deleted file mode 100644
index a51904c..0000000
--- a/chrome/browser/extensions/extension_toolbar_model_factory.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/extension_toolbar_model_factory.h"
-
-#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_prefs_factory.h"
-#include "extensions/browser/extensions_browser_client.h"
-
-namespace extensions {
-
-// static
-ExtensionToolbarModel* ExtensionToolbarModelFactory::GetForProfile(
- Profile* profile) {
- return static_cast<ExtensionToolbarModel*>(
- GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-ExtensionToolbarModelFactory* ExtensionToolbarModelFactory::GetInstance() {
- return Singleton<ExtensionToolbarModelFactory>::get();
-}
-
-ExtensionToolbarModelFactory::ExtensionToolbarModelFactory()
- : BrowserContextKeyedServiceFactory(
- "ExtensionToolbarModel",
- BrowserContextDependencyManager::GetInstance()) {
- DependsOn(ExtensionPrefsFactory::GetInstance());
- DependsOn(ExtensionActionAPI::GetFactoryInstance());
-}
-
-ExtensionToolbarModelFactory::~ExtensionToolbarModelFactory() {}
-
-KeyedService* ExtensionToolbarModelFactory::BuildServiceInstanceFor(
- content::BrowserContext* context) const {
- return new ExtensionToolbarModel(
- Profile::FromBrowserContext(context),
- ExtensionPrefsFactory::GetForBrowserContext(context));
-}
-
-content::BrowserContext* ExtensionToolbarModelFactory::GetBrowserContextToUse(
- content::BrowserContext* context) const {
- return context;
-}
-
-bool ExtensionToolbarModelFactory::ServiceIsCreatedWithBrowserContext() const {
- return true;
-}
-
-bool ExtensionToolbarModelFactory::ServiceIsNULLWhileTesting() const {
- return true;
-}
-
-} // namespace extensions
diff --git a/chrome/browser/extensions/extension_toolbar_model_unittest.cc b/chrome/browser/extensions/extension_toolbar_model_unittest.cc
deleted file mode 100644
index 1d175d7d..0000000
--- a/chrome/browser/extensions/extension_toolbar_model_unittest.cc
+++ /dev/null
@@ -1,1210 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_action_test_util.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_service_test_base.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
-#include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/test_extension_dir.h"
-#include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/extensions/unpacked_installer.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sessions/session_tab_helper.h"
-#include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
-#include "chrome/common/extensions/api/extension_action/action_info.h"
-#include "components/crx_file/id_util.h"
-#include "content/public/test/test_renderer_host.h"
-#include "content/public/test/web_contents_tester.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/pref_names.h"
-#include "extensions/browser/test_extension_registry_observer.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/feature_switch.h"
-#include "extensions/common/value_builder.h"
-
-namespace extensions {
-
-namespace {
-
-// A simple observer that tracks the number of times certain events occur.
-class ExtensionToolbarModelTestObserver
- : public ExtensionToolbarModel::Observer {
- public:
- explicit ExtensionToolbarModelTestObserver(ExtensionToolbarModel* model);
- ~ExtensionToolbarModelTestObserver() override;
-
- size_t inserted_count() const { return inserted_count_; }
- size_t removed_count() const { return removed_count_; }
- size_t moved_count() const { return moved_count_; }
- int highlight_mode_count() const { return highlight_mode_count_; }
- size_t initialized_count() const { return initialized_count_; }
-
- private:
- // ExtensionToolbarModel::Observer:
- void OnToolbarExtensionAdded(const Extension* extension, int index) override {
- ++inserted_count_;
- }
-
- void OnToolbarExtensionRemoved(const Extension* extension) override {
- ++removed_count_;
- }
-
- void OnToolbarExtensionMoved(const Extension* extension, int index) override {
- ++moved_count_;
- }
-
- void OnToolbarExtensionUpdated(const Extension* extension) override {}
-
- bool ShowExtensionActionPopup(const Extension* extension,
- bool grant_active_tab) override {
- return false;
- }
-
- void OnToolbarVisibleCountChanged() override {}
-
- void OnToolbarHighlightModeChanged(bool is_highlighting) override {
- // Add one if highlighting, subtract one if not.
- highlight_mode_count_ += is_highlighting ? 1 : -1;
- }
-
- void OnToolbarModelInitialized() override { ++initialized_count_; }
-
- Browser* GetBrowser() override { return NULL; }
-
- ExtensionToolbarModel* model_;
-
- size_t inserted_count_;
- size_t removed_count_;
- size_t moved_count_;
- // Int because it could become negative (if something goes wrong).
- int highlight_mode_count_;
- size_t initialized_count_;
-
- DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarModelTestObserver);
-};
-
-ExtensionToolbarModelTestObserver::ExtensionToolbarModelTestObserver(
- ExtensionToolbarModel* model) : model_(model),
- inserted_count_(0),
- removed_count_(0),
- moved_count_(0),
- highlight_mode_count_(0),
- initialized_count_(0) {
- model_->AddObserver(this);
-}
-
-ExtensionToolbarModelTestObserver::~ExtensionToolbarModelTestObserver() {
- model_->RemoveObserver(this);
-}
-
-} // namespace
-
-class ExtensionToolbarModelUnitTest : public ExtensionServiceTestBase {
- protected:
- // Initialize the ExtensionService, ExtensionToolbarModel, and
- // ExtensionSystem.
- void Init();
-
- void TearDown() override;
-
- // Adds or removes the given |extension| and verify success.
- testing::AssertionResult AddExtension(
- const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
- testing::AssertionResult RemoveExtension(
- const scoped_refptr<const Extension>& extension) WARN_UNUSED_RESULT;
-
- // Adds three extensions, all with browser actions.
- testing::AssertionResult AddBrowserActionExtensions() WARN_UNUSED_RESULT;
-
- // Adds three extensions, one each for browser action, page action, and no
- // action, and are added in that order.
- testing::AssertionResult AddActionExtensions() WARN_UNUSED_RESULT;
-
- // Returns the extension at the given index in the toolbar model, or NULL
- // if one does not exist.
- // If |model| is specified, it is used. Otherwise, this defaults to
- // |toolbar_model_|.
- const Extension* GetExtensionAtIndex(
- size_t index, const ExtensionToolbarModel* model) const;
- const Extension* GetExtensionAtIndex(size_t index) const;
-
- ExtensionToolbarModel* toolbar_model() { return toolbar_model_; }
-
- const ExtensionToolbarModelTestObserver* observer() const {
- return model_observer_.get();
- }
- size_t num_toolbar_items() const {
- return toolbar_model_->toolbar_items().size();
- }
- const Extension* browser_action_a() const { return browser_action_a_.get(); }
- const Extension* browser_action_b() const { return browser_action_b_.get(); }
- const Extension* browser_action_c() const { return browser_action_c_.get(); }
- const Extension* browser_action() const {
- return browser_action_extension_.get();
- }
- const Extension* page_action() const { return page_action_extension_.get(); }
- const Extension* no_action() const { return no_action_extension_.get(); }
-
- private:
- // Verifies that all extensions in |extensions| are added successfully.
- testing::AssertionResult AddAndVerifyExtensions(
- const ExtensionList& extensions);
-
- // The toolbar model associated with the testing profile.
- ExtensionToolbarModel* toolbar_model_;
-
- // The test observer to track events. Must come after toolbar_model_ so that
- // it is destroyed and removes itself as an observer first.
- scoped_ptr<ExtensionToolbarModelTestObserver> model_observer_;
-
- // Sample extensions with only browser actions.
- scoped_refptr<const Extension> browser_action_a_;
- scoped_refptr<const Extension> browser_action_b_;
- scoped_refptr<const Extension> browser_action_c_;
-
- // Sample extensions with different kinds of actions.
- scoped_refptr<const Extension> browser_action_extension_;
- scoped_refptr<const Extension> page_action_extension_;
- scoped_refptr<const Extension> no_action_extension_;
-};
-
-void ExtensionToolbarModelUnitTest::Init() {
- InitializeEmptyExtensionService();
- toolbar_model_ =
- extension_action_test_util::CreateToolbarModelForProfile(profile());
- model_observer_.reset(new ExtensionToolbarModelTestObserver(toolbar_model_));
-}
-
-void ExtensionToolbarModelUnitTest::TearDown() {
- model_observer_.reset();
- ExtensionServiceTestBase::TearDown();
-}
-
-testing::AssertionResult ExtensionToolbarModelUnitTest::AddExtension(
- const scoped_refptr<const Extension>& extension) {
- if (registry()->enabled_extensions().GetByID(extension->id())) {
- return testing::AssertionFailure() << "Extension " << extension->name() <<
- " already installed!";
- }
- service()->AddExtension(extension.get());
- if (!registry()->enabled_extensions().GetByID(extension->id())) {
- return testing::AssertionFailure() << "Failed to install extension: " <<
- extension->name();
- }
- return testing::AssertionSuccess();
-}
-
-testing::AssertionResult ExtensionToolbarModelUnitTest::RemoveExtension(
- const scoped_refptr<const Extension>& extension) {
- if (!registry()->enabled_extensions().GetByID(extension->id())) {
- return testing::AssertionFailure() << "Extension " << extension->name() <<
- " not installed!";
- }
- service()->UnloadExtension(extension->id(),
- UnloadedExtensionInfo::REASON_DISABLE);
- if (registry()->enabled_extensions().GetByID(extension->id())) {
- return testing::AssertionFailure() << "Failed to unload extension: " <<
- extension->name();
- }
- return testing::AssertionSuccess();
-}
-
-testing::AssertionResult ExtensionToolbarModelUnitTest::AddActionExtensions() {
- browser_action_extension_ = extension_action_test_util::CreateActionExtension(
- "browser_action", extension_action_test_util::BROWSER_ACTION);
- page_action_extension_ = extension_action_test_util::CreateActionExtension(
- "page_action", extension_action_test_util::PAGE_ACTION);
- no_action_extension_ = extension_action_test_util::CreateActionExtension(
- "no_action", extension_action_test_util::NO_ACTION);
-
- ExtensionList extensions;
- extensions.push_back(browser_action_extension_);
- extensions.push_back(page_action_extension_);
- extensions.push_back(no_action_extension_);
-
- return AddAndVerifyExtensions(extensions);
-}
-
-testing::AssertionResult
-ExtensionToolbarModelUnitTest::AddBrowserActionExtensions() {
- browser_action_a_ = extension_action_test_util::CreateActionExtension(
- "browser_actionA", extension_action_test_util::BROWSER_ACTION);
- browser_action_b_ = extension_action_test_util::CreateActionExtension(
- "browser_actionB", extension_action_test_util::BROWSER_ACTION);
- browser_action_c_ = extension_action_test_util::CreateActionExtension(
- "browser_actionC", extension_action_test_util::BROWSER_ACTION);
-
- ExtensionList extensions;
- extensions.push_back(browser_action_a_);
- extensions.push_back(browser_action_b_);
- extensions.push_back(browser_action_c_);
-
- return AddAndVerifyExtensions(extensions);
-}
-
-const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
- size_t index, const ExtensionToolbarModel* model) const {
- return index < model->toolbar_items().size()
- ? model->toolbar_items()[index].get()
- : NULL;
-}
-
-const Extension* ExtensionToolbarModelUnitTest::GetExtensionAtIndex(
- size_t index) const {
- return GetExtensionAtIndex(index, toolbar_model_);
-}
-
-testing::AssertionResult ExtensionToolbarModelUnitTest::AddAndVerifyExtensions(
- const ExtensionList& extensions) {
- for (ExtensionList::const_iterator iter = extensions.begin();
- iter != extensions.end(); ++iter) {
- if (!AddExtension(*iter)) {
- return testing::AssertionFailure() << "Failed to install extension: " <<
- (*iter)->name();
- }
- }
- return testing::AssertionSuccess();
-}
-
-// A basic test for extensions with browser actions showing up in the toolbar.
-TEST_F(ExtensionToolbarModelUnitTest, BasicExtensionToolbarModelTest) {
- Init();
-
- // Load an extension with no browser action.
- scoped_refptr<const Extension> extension1 =
- extension_action_test_util::CreateActionExtension(
- "no_action", extension_action_test_util::NO_ACTION);
- ASSERT_TRUE(AddExtension(extension1));
-
- // This extension should not be in the model (has no browser action).
- EXPECT_EQ(0u, observer()->inserted_count());
- EXPECT_EQ(0u, num_toolbar_items());
- EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
-
- // Load an extension with a browser action.
- scoped_refptr<const Extension> extension2 =
- extension_action_test_util::CreateActionExtension(
- "browser_action", extension_action_test_util::BROWSER_ACTION);
- ASSERT_TRUE(AddExtension(extension2));
-
- // We should now find our extension in the model.
- EXPECT_EQ(1u, observer()->inserted_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
-
- // Should be a no-op, but still fires the events.
- toolbar_model()->MoveExtensionIcon(extension2->id(), 0);
- EXPECT_EQ(1u, observer()->moved_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(extension2.get(), GetExtensionAtIndex(0u));
-
- // Remove the extension and verify.
- ASSERT_TRUE(RemoveExtension(extension2));
- EXPECT_EQ(1u, observer()->removed_count());
- EXPECT_EQ(0u, num_toolbar_items());
- EXPECT_EQ(NULL, GetExtensionAtIndex(0u));
-}
-
-// Test various different reorderings, removals, and reinsertions.
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarReorderAndReinsert) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
-
- // Verify the three extensions are in the model in the proper order.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // Order is now A, B, C. Let's put C first.
- toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 0);
- EXPECT_EQ(1u, observer()->moved_count());
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
-
- // Order is now C, A, B. Let's put A last.
- toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 2);
- EXPECT_EQ(2u, observer()->moved_count());
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
-
- // Order is now C, B, A. Let's remove B.
- ASSERT_TRUE(RemoveExtension(browser_action_b()));
- EXPECT_EQ(1u, observer()->removed_count());
- EXPECT_EQ(2u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1u));
-
- // Load extension B again.
- ASSERT_TRUE(AddExtension(browser_action_b()));
-
- // Extension B loaded again.
- EXPECT_EQ(4u, observer()->inserted_count());
- EXPECT_EQ(3u, num_toolbar_items());
- // Make sure it gets its old spot in the list.
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
-
- // Unload B again.
- ASSERT_TRUE(RemoveExtension(browser_action_b()));
- EXPECT_EQ(2u, observer()->removed_count());
- EXPECT_EQ(2u, num_toolbar_items());
-
- // Order is now C, A. Flip it.
- toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 0);
- EXPECT_EQ(3u, observer()->moved_count());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
-
- // Move A to the location it already occupies.
- toolbar_model()->MoveExtensionIcon(browser_action_a()->id(), 0);
- EXPECT_EQ(4u, observer()->moved_count());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
-
- // Order is now A, C. Remove C.
- ASSERT_TRUE(RemoveExtension(browser_action_c()));
- EXPECT_EQ(3u, observer()->removed_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
-
- // Load extension C again.
- ASSERT_TRUE(AddExtension(browser_action_c()));
-
- // Extension C loaded again.
- EXPECT_EQ(5u, observer()->inserted_count());
- EXPECT_EQ(2u, num_toolbar_items());
- // Make sure it gets its old spot in the list (at the very end).
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
-}
-
-// Test that order persists after unloading and disabling, but not across
-// uninstallation.
-TEST_F(ExtensionToolbarModelUnitTest,
- ExtensionToolbarUnloadDisableAndUninstall) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
-
- // Verify the three extensions are in the model in the proper order: A, B, C.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // Unload B, then C, then A, and then reload C, then A, then B.
- ASSERT_TRUE(RemoveExtension(browser_action_b()));
- ASSERT_TRUE(RemoveExtension(browser_action_c()));
- ASSERT_TRUE(RemoveExtension(browser_action_a()));
- EXPECT_EQ(0u, num_toolbar_items()); // Sanity check: all gone?
- ASSERT_TRUE(AddExtension(browser_action_c()));
- ASSERT_TRUE(AddExtension(browser_action_a()));
- ASSERT_TRUE(AddExtension(browser_action_b()));
- EXPECT_EQ(3u, num_toolbar_items()); // Sanity check: all back?
- EXPECT_EQ(0u, observer()->moved_count());
-
- // Even though we unloaded and reloaded in a different order, the original
- // order (A, B, C) should be preserved.
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // Disabling extensions should also preserve order.
- service()->DisableExtension(browser_action_b()->id(),
- Extension::DISABLE_USER_ACTION);
- service()->DisableExtension(browser_action_c()->id(),
- Extension::DISABLE_USER_ACTION);
- service()->DisableExtension(browser_action_a()->id(),
- Extension::DISABLE_USER_ACTION);
- service()->EnableExtension(browser_action_c()->id());
- service()->EnableExtension(browser_action_a()->id());
- service()->EnableExtension(browser_action_b()->id());
-
- // Make sure we still get the original A, B, C order.
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // Move browser_action_b() to be first.
- toolbar_model()->MoveExtensionIcon(browser_action_b()->id(), 0);
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
-
- // Uninstall Extension B.
- service()->UninstallExtension(browser_action_b()->id(),
- UNINSTALL_REASON_FOR_TESTING,
- base::Bind(&base::DoNothing),
- NULL); // Ignore error.
- // List contains only A and C now. Validate that.
- EXPECT_EQ(2u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
-
- ASSERT_TRUE(AddExtension(browser_action_b()));
-
- // Make sure Extension B is _not_ first (its old position should have been
- // forgotten at uninstall time). Order should be A, C, B.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ReorderOnPrefChange) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
- EXPECT_EQ(3u, num_toolbar_items());
-
- // Change the value of the toolbar preference.
- ExtensionIdList new_order;
- new_order.push_back(browser_action_c()->id());
- new_order.push_back(browser_action_b()->id());
- ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
-
- // Verify order is changed.
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
-}
-
-// Test that new extensions are always visible on installation and inserted at
-// the "end" of the visible section.
-TEST_F(ExtensionToolbarModelUnitTest, NewToolbarExtensionsAreVisible) {
- Init();
-
- // Three extensions with actions.
- scoped_refptr<const Extension> extension_a =
- extension_action_test_util::CreateActionExtension(
- "a", extension_action_test_util::BROWSER_ACTION);
- scoped_refptr<const Extension> extension_b =
- extension_action_test_util::CreateActionExtension(
- "b", extension_action_test_util::BROWSER_ACTION);
- scoped_refptr<const Extension> extension_c =
- extension_action_test_util::CreateActionExtension(
- "c", extension_action_test_util::BROWSER_ACTION);
- scoped_refptr<const Extension> extension_d =
- extension_action_test_util::CreateActionExtension(
- "d", extension_action_test_util::BROWSER_ACTION);
-
- // We should start off without any extensions.
- EXPECT_EQ(0u, num_toolbar_items());
- EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
-
- // Add one extension. It should be visible.
- service()->AddExtension(extension_a.get());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(0u));
-
- // Hide all extensions.
- toolbar_model()->SetVisibleIconCount(0);
- EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
-
- // Add a new extension - it should be visible, so it should be in the first
- // index. The other extension should remain hidden.
- service()->AddExtension(extension_b.get());
- EXPECT_EQ(2u, num_toolbar_items());
- EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
-
- // Show all extensions.
- toolbar_model()->SetVisibleIconCount(2);
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
-
- // Add the third extension. Since all extensions are visible, it should go in
- // the last index.
- service()->AddExtension(extension_c.get());
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
- EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_c.get(), GetExtensionAtIndex(2u));
-
- // Hide one extension (two remaining visible).
- toolbar_model()->SetVisibleIconCount(2);
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-
- // Add a fourth extension. It should go at the end of the visible section and
- // be visible, so it increases visible count by 1, and goes into the third
- // index. The hidden extension should remain hidden.
- service()->AddExtension(extension_d.get());
- EXPECT_EQ(4u, num_toolbar_items());
- EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_d.get(), GetExtensionAtIndex(2u));
- EXPECT_EQ(extension_c.get(), GetExtensionAtIndex(3u));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightMode) {
- Init();
-
- EXPECT_FALSE(toolbar_model()->HighlightExtensions(
- ExtensionIdList(), ExtensionToolbarModel::HIGHLIGHT_WARNING));
- EXPECT_EQ(0, observer()->highlight_mode_count());
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
- EXPECT_EQ(3u, num_toolbar_items());
-
- // Start with a visible count of 2 (non-zero, and not all).
- toolbar_model()->SetVisibleIconCount(2u);
-
- // Highlight one extension.
- ExtensionIdList extension_ids;
- extension_ids.push_back(browser_action_b()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(1, observer()->highlight_mode_count());
- EXPECT_TRUE(toolbar_model()->is_highlighting());
-
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
- EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
-
- // Stop highlighting.
- toolbar_model()->StopHighlighting();
- EXPECT_EQ(0, observer()->highlight_mode_count());
- EXPECT_FALSE(toolbar_model()->is_highlighting());
-
- // Verify that the extensions are back to normal.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-
- // Call stop highlighting a second time (shouldn't be notified).
- toolbar_model()->StopHighlighting();
- EXPECT_EQ(0, observer()->highlight_mode_count());
- EXPECT_FALSE(toolbar_model()->is_highlighting());
-
- // Highlight all extensions.
- extension_ids.clear();
- extension_ids.push_back(browser_action_a()->id());
- extension_ids.push_back(browser_action_b()->id());
- extension_ids.push_back(browser_action_c()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(1, observer()->highlight_mode_count());
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
- EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
- // Even though the visible count is 3, we shouldn't adjust the stored
- // preference.
- EXPECT_EQ(2, profile()->GetPrefs()->GetInteger(pref_names::kToolbarSize));
-
- // Highlight only extension b (shrink the highlight list).
- extension_ids.clear();
- extension_ids.push_back(browser_action_b()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(2, observer()->highlight_mode_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
-
- // Highlight extensions a and b (grow the highlight list).
- extension_ids.clear();
- extension_ids.push_back(browser_action_a()->id());
- extension_ids.push_back(browser_action_b()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(3, observer()->highlight_mode_count());
- EXPECT_EQ(2u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
-
- // Highlight no extensions (empty the highlight list).
- extension_ids.clear();
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(2, observer()->highlight_mode_count());
- EXPECT_FALSE(toolbar_model()->is_highlighting());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // Our toolbar size should be back to normal.
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(2, profile()->GetPrefs()->GetInteger(pref_names::kToolbarSize));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeRemove) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
- EXPECT_EQ(3u, num_toolbar_items());
-
- // Highlight two of the extensions.
- ExtensionIdList extension_ids;
- extension_ids.push_back(browser_action_a()->id());
- extension_ids.push_back(browser_action_b()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1, observer()->highlight_mode_count());
- EXPECT_EQ(2u, num_toolbar_items());
-
- // Disable one of them - only one should remain highlighted.
- service()->DisableExtension(browser_action_a()->id(),
- Extension::DISABLE_USER_ACTION);
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u));
-
- // Uninstall the remaining highlighted extension. This should result in
- // highlight mode exiting.
- service()->UninstallExtension(browser_action_b()->id(),
- UNINSTALL_REASON_FOR_TESTING,
- base::Bind(&base::DoNothing),
- NULL); // Ignore error.
- EXPECT_FALSE(toolbar_model()->is_highlighting());
- EXPECT_EQ(0, observer()->highlight_mode_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
-
- // Test that removing an unhighlighted extension still works.
- // Reinstall extension b, and then highlight extension c.
- ASSERT_TRUE(AddExtension(browser_action_b()));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- extension_ids.clear();
- extension_ids.push_back(browser_action_c()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_EQ(1, observer()->highlight_mode_count());
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
-
- // Uninstalling b should not have visible impact.
- service()->UninstallExtension(browser_action_b()->id(),
- UNINSTALL_REASON_FOR_TESTING,
- base::Bind(&base::DoNothing),
- NULL); // Ignore error.
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1, observer()->highlight_mode_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
-
- // When we stop, only extension c should remain.
- toolbar_model()->StopHighlighting();
- EXPECT_FALSE(toolbar_model()->is_highlighting());
- EXPECT_EQ(0, observer()->highlight_mode_count());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightModeAdd) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
- EXPECT_EQ(3u, num_toolbar_items());
-
- // Remove one (down to two).
- ASSERT_TRUE(RemoveExtension(browser_action_c()));
-
- // Highlight one of the two extensions.
- ExtensionIdList extension_ids;
- extension_ids.push_back(browser_action_a()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, ExtensionToolbarModel::HIGHLIGHT_WARNING);
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
-
- // Adding a new extension should have no visible effect.
- ASSERT_TRUE(AddExtension(browser_action_c()));
- EXPECT_TRUE(toolbar_model()->is_highlighting());
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
-
- // When we stop highlighting, we should see the new extension show up.
- toolbar_model()->StopHighlighting();
- EXPECT_FALSE(toolbar_model()->is_highlighting());
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-}
-
-// Test that the extension toolbar maintains the proper size, even after a pref
-// change.
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarSizeAfterPrefChange) {
- Init();
-
- // Add the three browser action extensions.
- ASSERT_TRUE(AddBrowserActionExtensions());
- EXPECT_EQ(3u, num_toolbar_items());
-
- // Should be at max size.
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
- EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
- toolbar_model()->OnExtensionToolbarPrefChange();
- // Should still be at max size.
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
- EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
-}
-
-// Test that, in the absence of the extension-action-redesign switch, the
-// model only contains extensions with browser actions.
-TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesNoSwitch) {
- Init();
- ASSERT_TRUE(AddActionExtensions());
-
- EXPECT_EQ(1u, num_toolbar_items());
- EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
-}
-
-// Test that, with the extension-action-redesign switch, the model contains
-// all types of extensions, except those which should not be displayed on the
-// toolbar (like component extensions).
-TEST_F(ExtensionToolbarModelUnitTest, TestToolbarExtensionTypesSwitch) {
- FeatureSwitch::ScopedOverride enable_redesign(
- FeatureSwitch::extension_action_redesign(), true);
- Init();
- ASSERT_TRUE(AddActionExtensions());
-
- // With the switch on, extensions with page actions and no action should also
- // be displayed in the toolbar.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action(), GetExtensionAtIndex(0u));
- EXPECT_EQ(page_action(), GetExtensionAtIndex(1u));
- EXPECT_EQ(no_action(), GetExtensionAtIndex(2u));
-}
-
-// Test that hiding actions on the toolbar results in their removal from the
-// model when the redesign switch is not enabled.
-TEST_F(ExtensionToolbarModelUnitTest,
- ExtensionToolbarActionsVisibilityNoSwitch) {
- Init();
-
- ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile());
-
- ASSERT_TRUE(AddBrowserActionExtensions());
- // Sanity check: Order should start as A B C.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-
- // By default, all actions should be visible.
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_a()->id()));
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id()));
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_c()->id()));
-
- // Hiding an action should result in its removal from the toolbar.
- action_api->SetBrowserActionVisibility(browser_action_b()->id(), false);
- EXPECT_FALSE(action_api->GetBrowserActionVisibility(
- browser_action_b()->id()));
- // Thus, there should now only be two items on the toolbar - A and C.
- EXPECT_EQ(2u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
-
- // Resetting the visibility to 'true' should result in the extension being
- // added back at its original position.
- action_api->SetBrowserActionVisibility(browser_action_b()->id(), true);
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id()));
- // So the toolbar order should be A B C.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2u));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarIncognitoModeTest) {
- Init();
- ASSERT_TRUE(AddBrowserActionExtensions());
-
- // Give two extensions incognito access.
- // Note: We use ExtensionPrefs::SetIsIncognitoEnabled instead of
- // util::SetIsIncognitoEnabled because the latter tries to reload the
- // extension, which requries a filepath associated with the extension (and,
- // for this test, reloading the extension is irrelevant to us).
- ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile());
- extension_prefs->SetIsIncognitoEnabled(browser_action_b()->id(), true);
- extension_prefs->SetIsIncognitoEnabled(browser_action_c()->id(), true);
-
- util::SetIsIncognitoEnabled(browser_action_b()->id(), profile(), true);
- util::SetIsIncognitoEnabled(browser_action_c()->id(), profile(), true);
-
- // Move C to the second index.
- toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 1u);
- // Set visible count to 2 so that c is overflowed. State is A C [B].
- toolbar_model()->SetVisibleIconCount(2);
- EXPECT_EQ(1u, observer()->moved_count());
-
- // Get an incognito profile and toolbar.
- ExtensionToolbarModel* incognito_model =
- extension_action_test_util::CreateToolbarModelForProfile(
- profile()->GetOffTheRecordProfile());
-
- ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
- EXPECT_EQ(0u, incognito_observer.moved_count());
-
- // We should have two items, C and B, and the order should be preserved from
- // the original model.
- EXPECT_EQ(2u, incognito_model->toolbar_items().size());
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(0u, incognito_model));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1u, incognito_model));
-
- // Extensions in the overflow menu in the regular toolbar should remain in
- // overflow in the incognito toolbar. So, we should have C [B].
- EXPECT_EQ(1u, incognito_model->visible_icon_count());
- // The regular model should still have two icons visible.
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-
- // Changing the incognito model size should not affect the regular model.
- incognito_model->SetVisibleIconCount(0);
- EXPECT_EQ(0u, incognito_model->visible_icon_count());
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-
- // Expanding the incognito model to 2 should register as "all icons"
- // since it is all of the incognito-enabled extensions.
- incognito_model->SetVisibleIconCount(2u);
- EXPECT_EQ(2u, incognito_model->visible_icon_count());
- EXPECT_TRUE(incognito_model->all_icons_visible());
-
- // Moving icons in the incognito toolbar should not affect the regular
- // toolbar. Incognito currently has C B...
- incognito_model->MoveExtensionIcon(browser_action_b()->id(), 0u);
- // So now it should be B C...
- EXPECT_EQ(1u, incognito_observer.moved_count());
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0u, incognito_model));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u, incognito_model));
- // ... and the regular toolbar should be unaffected.
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0u));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1u));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2u));
-
- // Similarly, the observer for the regular model should not have received
- // any updates.
- EXPECT_EQ(1u, observer()->moved_count());
-
- // And performing moves on the regular model should have no effect on the
- // incognito model or its observers.
- toolbar_model()->MoveExtensionIcon(browser_action_c()->id(), 2u);
- EXPECT_EQ(2u, observer()->moved_count());
- EXPECT_EQ(1u, incognito_observer.moved_count());
-}
-
-// Test that enabling extensions incognito with an active incognito profile
-// works.
-TEST_F(ExtensionToolbarModelUnitTest,
- ExtensionToolbarIncognitoEnableExtension) {
- Init();
-
- const char* kManifest =
- "{"
- " \"name\": \"%s\","
- " \"version\": \"1.0\","
- " \"manifest_version\": 2,"
- " \"browser_action\": {}"
- "}";
-
- // For this test, we need to have "real" extension files, because we need to
- // be able to reload them during the incognito process. Since the toolbar
- // needs to be notified of the reload, we need it this time (as opposed to
- // above, where we simply set the prefs before the incognito bar was
- // created.
- TestExtensionDir dir1;
- dir1.WriteManifest(base::StringPrintf(kManifest, "incognito1"));
- TestExtensionDir dir2;
- dir2.WriteManifest(base::StringPrintf(kManifest, "incognito2"));
-
- TestExtensionDir* dirs[] = { &dir1, &dir2 };
- const Extension* extensions[] = { nullptr, nullptr };
- for (size_t i = 0; i < arraysize(dirs); ++i) {
- // The extension id will be calculated from the file path; we need this to
- // wait for the extension to load.
- base::FilePath path_for_id =
- base::MakeAbsoluteFilePath(dirs[i]->unpacked_path());
- std::string id = crx_file::id_util::GenerateIdForPath(path_for_id);
- TestExtensionRegistryObserver observer(registry(), id);
- UnpackedInstaller::Create(service())->Load(dirs[i]->unpacked_path());
- observer.WaitForExtensionLoaded();
- extensions[i] = registry()->enabled_extensions().GetByID(id);
- ASSERT_TRUE(extensions[i]);
- }
-
- // For readability, alias to A and B. Since we'll be reloading these
- // extensions, we also can't rely on pointers.
- std::string extension_a = extensions[0]->id();
- std::string extension_b = extensions[1]->id();
-
- // The first model should have both extensions visible.
- EXPECT_EQ(2u, toolbar_model()->toolbar_items().size());
- EXPECT_EQ(extension_a, GetExtensionAtIndex(0)->id());
- EXPECT_EQ(extension_b, GetExtensionAtIndex(1)->id());
-
- // Set the model to only show one extension, so the order is A [B].
- toolbar_model()->SetVisibleIconCount(1u);
-
- // Get an incognito profile and toolbar.
- ExtensionToolbarModel* incognito_model =
- extension_action_test_util::CreateToolbarModelForProfile(
- profile()->GetOffTheRecordProfile());
- ExtensionToolbarModelTestObserver incognito_observer(incognito_model);
-
- // Right now, no extensions are enabled in incognito mode.
- EXPECT_EQ(0u, incognito_model->toolbar_items().size());
-
- // Set extension b (which is overflowed) to be enabled in incognito. This
- // results in b reloading, so wait for it.
- {
- TestExtensionRegistryObserver observer(registry(), extension_b);
- util::SetIsIncognitoEnabled(extension_b, profile(), true);
- observer.WaitForExtensionLoaded();
- }
-
- // Now, we should have one icon in the incognito bar. But, since B is
- // overflowed in the main bar, it shouldn't be visible.
- EXPECT_EQ(1u, incognito_model->toolbar_items().size());
- EXPECT_EQ(extension_b, GetExtensionAtIndex(0u, incognito_model)->id());
- EXPECT_EQ(0u, incognito_model->visible_icon_count());
-
- // Also enable extension a for incognito (again, wait for the reload).
- {
- TestExtensionRegistryObserver observer(registry(), extension_a);
- util::SetIsIncognitoEnabled(extension_a, profile(), true);
- observer.WaitForExtensionLoaded();
- }
-
- // Now, both extensions should be enabled in incognito mode. In addition, the
- // incognito toolbar should have expanded to show extension a (since it isn't
- // overflowed in the main bar).
- EXPECT_EQ(2u, incognito_model->toolbar_items().size());
- EXPECT_EQ(extension_a, GetExtensionAtIndex(0u, incognito_model)->id());
- EXPECT_EQ(extension_b, GetExtensionAtIndex(1u, incognito_model)->id());
- EXPECT_EQ(1u, incognito_model->visible_icon_count());
-}
-
-// Test that hiding actions on the toolbar results in sending them to the
-// overflow menu when the redesign switch is enabled.
-TEST_F(ExtensionToolbarModelUnitTest,
- ExtensionToolbarActionsVisibilityWithSwitch) {
- FeatureSwitch::ScopedOverride enable_redesign(
- FeatureSwitch::extension_action_redesign(), true);
- Init();
-
- // We choose to use all types of extensions here, since the misnamed
- // BrowserActionVisibility is now for toolbar visibility.
- ASSERT_TRUE(AddActionExtensions());
-
- // For readability, alias extensions A B C.
- const Extension* extension_a = browser_action();
- const Extension* extension_b = page_action();
- const Extension* extension_c = no_action();
-
- // Sanity check: Order should start as A B C, with all three visible.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
- EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_b, GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_c, GetExtensionAtIndex(2u));
-
- ExtensionActionAPI* action_api = ExtensionActionAPI::Get(profile());
-
- // By default, all actions should be visible.
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_a->id()));
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_c->id()));
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_b->id()));
-
- // Hiding an action should result in it being sent to the overflow menu.
- action_api->SetBrowserActionVisibility(extension_b->id(), false);
-
- // Thus, the order should be A C B, with B in the overflow.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_a, GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_c, GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
-
- // Hiding an extension's action should result in it being sent to the overflow
- // as well, but as the _first_ extension in the overflow.
- action_api->SetBrowserActionVisibility(extension_a->id(), false);
- // Thus, the order should be C A B, with A and B in the overflow.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
-
- // Resetting A's visibility to true should send it back to the visible icons
- // (and should grow visible icons by 1), but it should be added to the end of
- // the visible icon list (not to its original position).
- action_api->SetBrowserActionVisibility(extension_a->id(), true);
- // So order is C A B, with only B in the overflow.
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
- EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
-
- // Resetting B to be visible should make the order C A B, with no overflow.
- action_api->SetBrowserActionVisibility(extension_b->id(), true);
- EXPECT_EQ(3u, num_toolbar_items());
- EXPECT_TRUE(toolbar_model()->all_icons_visible());
- EXPECT_EQ(extension_c, GetExtensionAtIndex(0u));
- EXPECT_EQ(extension_a, GetExtensionAtIndex(1u));
- EXPECT_EQ(extension_b, GetExtensionAtIndex(2u));
-
- // Regression test for crbug.com/515963. Check that an extension's visibility
- // is updated when it is moved out because another extension was removed.
- toolbar_model()->SetVisibleIconCount(1);
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(action_api->GetBrowserActionVisibility(extension_a->id()));
- service()->DisableExtension(extension_c->id(),
- Extension::DISABLE_USER_ACTION);
- EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
- EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_a->id()));
-}
-
-// Test that observers receive no Added notifications until after the
-// ExtensionSystem has initialized.
-TEST_F(ExtensionToolbarModelUnitTest, ModelWaitsForExtensionSystemReady) {
- InitializeEmptyExtensionService();
- ExtensionToolbarModel* toolbar_model =
- extension_action_test_util::
- CreateToolbarModelForProfileWithoutWaitingForReady(profile());
- ExtensionToolbarModelTestObserver model_observer(toolbar_model);
- EXPECT_TRUE(AddBrowserActionExtensions());
-
- // Since the model hasn't been initialized (the ExtensionSystem::ready task
- // hasn't been run), there should be no insertion notifications.
- EXPECT_EQ(0u, model_observer.inserted_count());
- EXPECT_EQ(0u, model_observer.initialized_count());
- EXPECT_FALSE(toolbar_model->extensions_initialized());
-
- // Run the ready task.
- static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))->
- SetReady();
- // Run tasks posted to TestExtensionSystem.
- base::RunLoop().RunUntilIdle();
-
- // We should still have no insertions, but should have an initialized count.
- EXPECT_TRUE(toolbar_model->extensions_initialized());
- EXPECT_EQ(0u, model_observer.inserted_count());
- EXPECT_EQ(1u, model_observer.initialized_count());
-}
-
-// Check that the toolbar model correctly clears and reorders when it detects
-// a preference change.
-TEST_F(ExtensionToolbarModelUnitTest, ToolbarModelPrefChange) {
- Init();
-
- ASSERT_TRUE(AddBrowserActionExtensions());
-
- // We should start in the basic A B C order.
- ASSERT_TRUE(browser_action_a());
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2));
- // Record the difference between the inserted and removed counts. The actual
- // value of the counts is not important, but we need to be sure that if we
- // call to remove any, we also add them back.
- size_t inserted_and_removed_difference =
- observer()->inserted_count() - observer()->removed_count();
-
- // Assign a new order, B C A, and write it in the prefs.
- ExtensionIdList new_order;
- new_order.push_back(browser_action_b()->id());
- new_order.push_back(browser_action_c()->id());
- new_order.push_back(browser_action_a()->id());
- ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
-
- // Ensure everything has time to run.
- base::RunLoop().RunUntilIdle();
-
- // The new order should be reflected in the model.
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(0));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(1));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2));
- EXPECT_EQ(inserted_and_removed_difference,
- observer()->inserted_count() - observer()->removed_count());
-}
-
-TEST_F(ExtensionToolbarModelUnitTest, ComponentExtesionsAddedToEnd) {
- Init();
-
- ASSERT_TRUE(AddBrowserActionExtensions());
-
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(0));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(1));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(2));
-
- const char kName[] = "component";
- DictionaryBuilder manifest;
- manifest.Set("name", kName)
- .Set("description", "An extension")
- .Set("manifest_version", 2)
- .Set("version", "1.0.0")
- .Set("browser_action", DictionaryBuilder().Pass());
- scoped_refptr<const Extension> component_extension =
- ExtensionBuilder().SetManifest(manifest.Pass())
- .SetID(crx_file::id_util::GenerateId(kName))
- .SetLocation(Manifest::COMPONENT)
- .Build();
- service()->AddExtension(component_extension.get());
-
- EXPECT_EQ(component_extension.get(), GetExtensionAtIndex(0));
- EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(1));
- EXPECT_EQ(browser_action_b(), GetExtensionAtIndex(2));
- EXPECT_EQ(browser_action_c(), GetExtensionAtIndex(3));
-}
-
-TEST_F(ExtensionToolbarModelUnitTest,
- ToolbarModelHighlightsForToolbarRedesign) {
- FeatureSwitch::ScopedOverride enable_redesign(
- FeatureSwitch::extension_action_redesign(), true);
- InitializeEmptyExtensionService();
- EXPECT_TRUE(AddActionExtensions());
- ExtensionToolbarModel* toolbar_model =
- extension_action_test_util::CreateToolbarModelForProfile(profile());
- EXPECT_TRUE(toolbar_model);
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
- profile()));
- EXPECT_TRUE(toolbar_model->is_highlighting());
- EXPECT_EQ(ExtensionToolbarModel::HIGHLIGHT_INFO,
- toolbar_model->highlight_type());
- EXPECT_EQ(3u, toolbar_model->visible_icon_count());
- EXPECT_EQ(3u, toolbar_model->toolbar_items().size());
-
- scoped_ptr<ToolbarActionsBarBubbleDelegate> bubble(
- new ExtensionToolbarIconSurfacingBubbleDelegate(profile()));
- bubble->OnBubbleClosed(ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS);
-
- EXPECT_FALSE(toolbar_model->is_highlighting());
- EXPECT_EQ(ExtensionToolbarModel::HIGHLIGHT_NONE,
- toolbar_model->highlight_type());
-}
-
-} // namespace extensions
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
index cee2e9d..c50a6a3 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button_interactive_uitest.mm
@@ -10,7 +10,6 @@
#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
@@ -22,6 +21,7 @@
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "extensions/common/feature_switch.h"
#import "ui/events/test/cocoa_test_event_utils.h"
@@ -137,7 +137,7 @@ class BrowserActionButtonUiTest : public ExtensionBrowserTest {
toolbarController];
ASSERT_TRUE(toolbarController_);
wrenchMenuController_ = [toolbarController_ wrenchMenuController];
- model_ = extensions::ExtensionToolbarModel::Get(profile());
+ model_ = ToolbarActionsModel::Get(profile());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -155,7 +155,7 @@ class BrowserActionButtonUiTest : public ExtensionBrowserTest {
ToolbarController* toolbarController() { return toolbarController_; }
WrenchMenuController* wrenchMenuController() { return wrenchMenuController_; }
- extensions::ExtensionToolbarModel* model() { return model_; }
+ ToolbarActionsModel* model() { return model_; }
NSView* wrenchButton() { return [toolbarController_ wrenchButton]; }
private:
@@ -163,7 +163,7 @@ class BrowserActionButtonUiTest : public ExtensionBrowserTest {
ToolbarController* toolbarController_ = nil;
WrenchMenuController* wrenchMenuController_ = nil;
- extensions::ExtensionToolbarModel* model_ = nullptr;
+ ToolbarActionsModel* model_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(BrowserActionButtonUiTest);
};
diff --git a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
index 3130239..ad62cb0 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_actions_controller.mm
@@ -563,7 +563,7 @@ void ToolbarActionsBarBridge::ShowExtensionMessageBubble(
scoped_ptr<ui::NinePartImageIds> highlight;
if (toolbarActionsBar_->is_highlighting()) {
if (toolbarActionsBar_->highlight_type() ==
- extensions::ExtensionToolbarModel::HIGHLIGHT_INFO)
+ ToolbarActionsModel::HIGHLIGHT_INFO)
highlight.reset(
new ui::NinePartImageIds(IMAGE_GRID(IDR_TOOLBAR_ACTION_HIGHLIGHT)));
else
diff --git a/chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.cc b/chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.cc
index 63763c3..d355351 100644
--- a/chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.cc
+++ b/chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.cc
@@ -7,8 +7,8 @@
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "base/time/time.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/common/pref_names.h"
#include "extensions/common/feature_switch.h"
#include "grit/chromium_strings.h"
@@ -54,8 +54,7 @@ bool ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
return false;
}
- if (!extensions::ExtensionToolbarModel::Get(profile)->
- RedesignIsShowingNewIcons()) {
+ if (!ToolbarActionsModel::Get(profile)->RedesignIsShowingNewIcons()) {
// We only show the bubble if there are any new icons present - otherwise,
// the user won't see anything different, so we treat it as acknowledged.
AcknowledgeInPrefs(prefs);
@@ -103,5 +102,5 @@ void ExtensionToolbarIconSurfacingBubbleDelegate::OnBubbleClosed(
CloseAction action) {
if (action == CLOSE_EXECUTE)
AcknowledgeInPrefs(profile_->GetPrefs());
- extensions::ExtensionToolbarModel::Get(profile_)->StopHighlighting();
+ ToolbarActionsModel::Get(profile_)->StopHighlighting();
}
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
index a47a3c8..5860926 100644
--- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
+++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.cc
@@ -12,12 +12,12 @@
#include "chrome/browser/extensions/extension_action_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/ui/extensions/extension_action_view_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "components/crx_file/id_util.h"
#include "content/public/browser/notification_service.h"
#include "content/public/test/test_utils.h"
@@ -67,7 +67,7 @@ void BrowserActionsBarBrowserTest::SetUpCommandLine(
void BrowserActionsBarBrowserTest::SetUpOnMainThread() {
ExtensionBrowserTest::SetUpOnMainThread();
browser_actions_bar_.reset(new BrowserActionTestUtil(browser()));
- toolbar_model_ = extensions::ExtensionToolbarModel::Get(profile());
+ toolbar_model_ = ToolbarActionsModel::Get(profile());
}
void BrowserActionsBarBrowserTest::TearDownOnMainThread() {
@@ -147,19 +147,19 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest, MoveBrowserActions) {
EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
// Move C to first position. Order is C A B.
- toolbar_model()->MoveExtensionIcon(extension_c()->id(), 0);
+ toolbar_model()->MoveActionIcon(extension_c()->id(), 0);
EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
// Move B to third position. Order is still C A B.
- toolbar_model()->MoveExtensionIcon(extension_b()->id(), 2);
+ toolbar_model()->MoveActionIcon(extension_b()->id(), 2);
EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(2));
// Move B to middle position. Order is C B A.
- toolbar_model()->MoveExtensionIcon(extension_b()->id(), 1);
+ toolbar_model()->MoveActionIcon(extension_b()->id(), 1);
EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(0));
EXPECT_EQ(extension_b()->id(), browser_actions_bar()->GetExtensionId(1));
EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(2));
diff --git a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
index 58baa28..3dcf83c 100644
--- a/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
+++ b/chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h
@@ -13,10 +13,10 @@
namespace extensions {
class Extension;
-class ExtensionToolbarModel;
}
class BrowserActionTestUtil;
+class ToolbarActionsModel;
// A platform-independent browser test class for the browser actions bar.
class BrowserActionsBarBrowserTest : public ExtensionBrowserTest {
@@ -31,7 +31,7 @@ class BrowserActionsBarBrowserTest : public ExtensionBrowserTest {
BrowserActionTestUtil* browser_actions_bar() {
return browser_actions_bar_.get();
}
- extensions::ExtensionToolbarModel* toolbar_model() { return toolbar_model_; }
+ ToolbarActionsModel* toolbar_model() { return toolbar_model_; }
// Creates three different extensions, each with a browser action, and adds
// them to associated ExtensionService. These can then be accessed via
@@ -52,7 +52,7 @@ class BrowserActionsBarBrowserTest : public ExtensionBrowserTest {
scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;
// The associated toolbar model, weak.
- extensions::ExtensionToolbarModel* toolbar_model_;
+ ToolbarActionsModel* toolbar_model_;
// Extensions with browser actions used for testing.
scoped_refptr<const extensions::Extension> extension_a_;
diff --git a/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc b/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
index bd12ffc..51d882b 100644
--- a/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
+++ b/chrome/browser/ui/toolbar/component_toolbar_actions_browsertest.cc
@@ -7,64 +7,13 @@
#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h"
#include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "extensions/common/feature_switch.h"
-namespace {
-
-const char kMockId[] = "mock_action";
-
-class MockComponentToolbarActionsFactory
- : public ComponentToolbarActionsFactory {
- public:
- explicit MockComponentToolbarActionsFactory(Browser* browser);
- virtual ~MockComponentToolbarActionsFactory();
-
- // ComponentToolbarActionsFactory:
- ScopedVector<ToolbarActionViewController>
- GetComponentToolbarActions(Browser* browser) override;
-
- const std::vector<std::string> action_ids() const {
- return action_ids_;
- }
-
- private:
- // A set of all action ids of created actions.
- std::vector<std::string> action_ids_;
-
- DISALLOW_COPY_AND_ASSIGN(MockComponentToolbarActionsFactory);
-};
-
-MockComponentToolbarActionsFactory::MockComponentToolbarActionsFactory(
- Browser* browser) {
- ComponentToolbarActionsFactory::SetTestingFactory(this);
-
- ScopedVector<ToolbarActionViewController> actions =
- GetComponentToolbarActions(browser);
- for (auto it = actions.begin(); it != actions.end(); ++it) {
- action_ids_.push_back((*it)->GetId());
- }
-}
-
-MockComponentToolbarActionsFactory::~MockComponentToolbarActionsFactory() {
- ComponentToolbarActionsFactory::SetTestingFactory(nullptr);
-}
-
-ScopedVector<ToolbarActionViewController>
-MockComponentToolbarActionsFactory::GetComponentToolbarActions(
- Browser* browser) {
- ScopedVector<ToolbarActionViewController> component_actions;
- TestToolbarActionViewController* action =
- new TestToolbarActionViewController(kMockId);
- component_actions.push_back(action);
- return component_actions.Pass();
-}
-
-} // namespace
-
class ComponentToolbarActionsBrowserTest : public InProcessBrowserTest {
protected:
ComponentToolbarActionsBrowserTest() {}
@@ -100,10 +49,11 @@ IN_PROC_BROWSER_TEST_F(ComponentToolbarActionsBrowserTest,
// Even though the method says "ExtensionId", this actually refers to any id
// for the action.
- EXPECT_EQ(kMockId, browser_actions_bar.GetExtensionId(0));
+ EXPECT_EQ(ComponentToolbarActionsFactory::kActionIdForTesting,
+ browser_actions_bar.GetExtensionId(0));
// There should only have been one created component action.
- const std::vector<std::string> action_ids = mock_factory()->action_ids();
+ const std::vector<std::string>& action_ids = mock_factory()->action_ids();
ASSERT_EQ(1u, action_ids.size());
const std::vector<ToolbarActionViewController*>& actions =
diff --git a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc
index 8f64c07..62b05a3 100644
--- a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc
+++ b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.cc
@@ -20,8 +20,13 @@ base::LazyInstance<ComponentToolbarActionsFactory> lazy_factory =
} // namespace
-ComponentToolbarActionsFactory::ComponentToolbarActionsFactory()
- : num_component_actions_(-1) {}
+// static
+const char ComponentToolbarActionsFactory::kMediaRouterActionId[] =
+ "media_router_action";
+const char ComponentToolbarActionsFactory::kActionIdForTesting[] =
+ "mock_action";
+
+ComponentToolbarActionsFactory::ComponentToolbarActionsFactory() {}
ComponentToolbarActionsFactory::~ComponentToolbarActionsFactory() {}
// static
@@ -29,6 +34,26 @@ ComponentToolbarActionsFactory* ComponentToolbarActionsFactory::GetInstance() {
return testing_factory_ ? testing_factory_ : &lazy_factory.Get();
}
+// static
+std::vector<std::string> ComponentToolbarActionsFactory::GetComponentIds() {
+ std::vector<std::string> component_ids;
+
+ // This is currently behind the extension-action-redesign flag, as it is
+ // designed for the new toolbar.
+ if (!extensions::FeatureSwitch::extension_action_redesign()->IsEnabled())
+ return component_ids;
+
+ if (testing_factory_) {
+ component_ids.push_back(
+ ComponentToolbarActionsFactory::kActionIdForTesting);
+ } else if (switches::MediaRouterEnabled()) {
+ component_ids.push_back(
+ ComponentToolbarActionsFactory::kMediaRouterActionId);
+ }
+
+ return component_ids;
+}
+
ScopedVector<ToolbarActionViewController>
ComponentToolbarActionsFactory::GetComponentToolbarActions(Browser* browser) {
ScopedVector<ToolbarActionViewController> component_actions;
@@ -45,23 +70,12 @@ ComponentToolbarActionsFactory::GetComponentToolbarActions(Browser* browser) {
// should be okay. If this changes, we should rethink this design to have,
// e.g., RegisterChromeAction().
-#if defined(ENABLE_MEDIA_ROUTER)
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- ::switches::kEnableMediaRouter)) {
+ if (switches::MediaRouterEnabled())
component_actions.push_back(new MediaRouterAction(browser));
- }
-#endif
return component_actions.Pass();
}
-int ComponentToolbarActionsFactory::GetNumComponentActions(Browser* browser) {
- if (num_component_actions_ == -1)
- num_component_actions_ = GetComponentToolbarActions(browser).size();
-
- return num_component_actions_;
-}
-
// static
void ComponentToolbarActionsFactory::SetTestingFactory(
ComponentToolbarActionsFactory* factory) {
diff --git a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.h b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.h
index 9a205ed..6bdee8e 100644
--- a/chrome/browser/ui/toolbar/component_toolbar_actions_factory.h
+++ b/chrome/browser/ui/toolbar/component_toolbar_actions_factory.h
@@ -17,28 +17,28 @@ class ToolbarActionViewController;
// components of chrome, such as ChromeCast.
class ComponentToolbarActionsFactory {
public:
+ // Component action IDs.
+ static const char kMediaRouterActionId[];
+ static const char kActionIdForTesting[]; // Only used for testing.
+
ComponentToolbarActionsFactory();
- ~ComponentToolbarActionsFactory();
+ virtual ~ComponentToolbarActionsFactory();
static ComponentToolbarActionsFactory* GetInstance();
- // Returns a collection of controllers for Chrome Actions. Declared virtual
- // for testing.
+ // Returns a vector of IDs of the component actions.
+ static std::vector<std::string> GetComponentIds();
+
+ // Returns a collection of controllers for component actions. Declared
+ // virtual for testing.
virtual ScopedVector<ToolbarActionViewController>
GetComponentToolbarActions(Browser* browser);
- // Returns the number of component actions.
- int GetNumComponentActions(Browser* browser);
-
// Sets the factory to use for testing purposes.
// Ownership remains with the caller.
static void SetTestingFactory(ComponentToolbarActionsFactory* factory);
private:
- // The number of component actions. Initially set to -1 to denote that the
- // count has not been checked yet.
- int num_component_actions_;
-
DISALLOW_COPY_AND_ASSIGN(ComponentToolbarActionsFactory);
};
diff --git a/chrome/browser/ui/toolbar/media_router_action.cc b/chrome/browser/ui/toolbar/media_router_action.cc
index cdfa0ae..eebe5de 100644
--- a/chrome/browser/ui/toolbar/media_router_action.cc
+++ b/chrome/browser/ui/toolbar/media_router_action.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/media/router/media_router_mojo_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
#include "chrome/browser/ui/toolbar/media_router_action_platform_delegate.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_delegate.h"
#include "chrome/grit/generated_resources.h"
@@ -26,22 +27,23 @@ using media_router::MediaRouterDialogController;
MediaRouterAction::MediaRouterAction(Browser* browser)
: media_router::IssuesObserver(GetMediaRouter(browser)),
media_router::MediaRoutesObserver(GetMediaRouter(browser)),
- id_("media_router_action"),
+ id_(ComponentToolbarActionsFactory::kMediaRouterActionId),
name_(l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_TITLE)),
- media_router_active_icon_(ui::ResourceBundle::GetSharedInstance().
- GetImageNamed(IDR_MEDIA_ROUTER_ACTIVE_ICON)),
- media_router_error_icon_(ui::ResourceBundle::GetSharedInstance().
- GetImageNamed(IDR_MEDIA_ROUTER_ERROR_ICON)),
- media_router_idle_icon_(ui::ResourceBundle::GetSharedInstance().
- GetImageNamed(IDR_MEDIA_ROUTER_IDLE_ICON)),
- media_router_warning_icon_(ui::ResourceBundle::GetSharedInstance().
- GetImageNamed(IDR_MEDIA_ROUTER_WARNING_ICON)),
+ media_router_active_icon_(
+ ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_MEDIA_ROUTER_ACTIVE_ICON)),
+ media_router_error_icon_(ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_MEDIA_ROUTER_ERROR_ICON)),
+ media_router_idle_icon_(ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_MEDIA_ROUTER_IDLE_ICON)),
+ media_router_warning_icon_(
+ ui::ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_MEDIA_ROUTER_WARNING_ICON)),
current_icon_(&media_router_idle_icon_),
has_local_route_(false),
delegate_(nullptr),
platform_delegate_(MediaRouterActionPlatformDelegate::Create(browser)),
- contextual_menu_(browser) {
-}
+ contextual_menu_(browser) {}
MediaRouterAction::~MediaRouterAction() {
}
@@ -101,7 +103,7 @@ ui::MenuModel* MediaRouterAction::GetContextMenu() {
}
bool MediaRouterAction::CanDrag() const {
- return false;
+ return true;
}
bool MediaRouterAction::ExecuteAction(bool by_user) {
@@ -112,6 +114,8 @@ bool MediaRouterAction::ExecuteAction(bool by_user) {
}
void MediaRouterAction::UpdateState() {
+ if (delegate_)
+ delegate_->UpdateState();
}
bool MediaRouterAction::DisabledClickOpensMenu() const {
diff --git a/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.cc b/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.cc
new file mode 100644
index 0000000..9694d90
--- /dev/null
+++ b/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.cc
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h"
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h"
+#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
+
+MockComponentToolbarActionsFactory::MockComponentToolbarActionsFactory(
+ Browser* browser) {
+ ComponentToolbarActionsFactory::SetTestingFactory(this);
+
+ ScopedVector<ToolbarActionViewController> actions =
+ GetComponentToolbarActions(browser);
+ for (const ToolbarActionViewController* action : actions)
+ action_ids_.push_back(action->GetId());
+}
+
+MockComponentToolbarActionsFactory::~MockComponentToolbarActionsFactory() {
+ ComponentToolbarActionsFactory::SetTestingFactory(nullptr);
+}
+
+ScopedVector<ToolbarActionViewController>
+MockComponentToolbarActionsFactory::GetComponentToolbarActions(
+ Browser* browser) {
+ ScopedVector<ToolbarActionViewController> component_actions;
+ component_actions.push_back(new TestToolbarActionViewController(
+ ComponentToolbarActionsFactory::kActionIdForTesting));
+ return component_actions.Pass();
+}
diff --git a/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h b/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h
new file mode 100644
index 0000000..abf9d1b
--- /dev/null
+++ b/chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TOOLBAR_MOCK_COMPONENT_TOOLBAR_ACTIONS_FACTORY_H_
+#define CHROME_BROWSER_UI_TOOLBAR_MOCK_COMPONENT_TOOLBAR_ACTIONS_FACTORY_H_
+
+#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+
+class Browser;
+
+// Mock class used to substitute ComponentToolbarActionsFactory in tests.
+class MockComponentToolbarActionsFactory
+ : public ComponentToolbarActionsFactory {
+ public:
+ explicit MockComponentToolbarActionsFactory(Browser* browser);
+ ~MockComponentToolbarActionsFactory() override;
+
+ // ComponentToolbarActionsFactory:
+ ScopedVector<ToolbarActionViewController> GetComponentToolbarActions(
+ Browser* browser) override;
+
+ const std::vector<std::string>& action_ids() const { return action_ids_; }
+
+ private:
+ std::vector<std::string> action_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockComponentToolbarActionsFactory);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_MOCK_COMPONENT_TOOLBAR_ACTIONS_FACTORY_H_
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index 98ef0f6..c9836b2 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -25,6 +25,7 @@
#include "chrome/common/pref_names.h"
#include "components/crx_file/id_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/runtime_data.h"
#include "extensions/common/extension.h"
@@ -119,7 +120,7 @@ ToolbarActionsBar::ToolbarActionsBar(ToolbarActionsBarDelegate* delegate,
ToolbarActionsBar* main_bar)
: delegate_(delegate),
browser_(browser),
- model_(extensions::ExtensionToolbarModel::Get(browser_->profile())),
+ model_(ToolbarActionsModel::Get(browser_->profile())),
main_bar_(main_bar),
platform_settings_(main_bar != nullptr),
popup_owner_(nullptr),
@@ -258,22 +259,9 @@ size_t ToolbarActionsBar::GetIconCount() const {
// icons than we have, and we should always have a view per item in the model.
// (The only exception is if this is in initialization.)
if (!toolbar_actions_.empty() && !suppress_layout_ &&
- model_->extensions_initialized()) {
- size_t num_extension_actions = 0u;
- for (ToolbarActionViewController* action : toolbar_actions_) {
- // No component action should ever have a valid extension id, so we can
- // use this to check the extension amount.
- if (crx_file::id_util::IdIsValid(action->GetId()))
- ++num_extension_actions;
- }
-
- int num_component_actions =
- ComponentToolbarActionsFactory::GetInstance()->
- GetNumComponentActions(browser_);
- size_t num_total_actions = num_extension_actions + num_component_actions;
-
- DCHECK_LE(visible_icons, num_total_actions);
- DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions);
+ model_->actions_initialized()) {
+ DCHECK_LE(visible_icons, toolbar_actions_.size());
+ DCHECK_EQ(model_->toolbar_items().size(), toolbar_actions_.size());
}
#endif
@@ -331,7 +319,7 @@ void ToolbarActionsBar::CreateActions() {
DCHECK(toolbar_actions_.empty());
// We wait for the extension system to be initialized before we add any
// actions, as they rely on the extension system to function.
- if (!model_ || !model_->extensions_initialized())
+ if (!model_ || !model_->actions_initialized())
return;
{
@@ -342,37 +330,14 @@ void ToolbarActionsBar::CreateActions() {
// We don't redraw the view while creating actions.
base::AutoReset<bool> layout_resetter(&suppress_layout_, true);
- // Extension actions come first.
- extensions::ExtensionActionManager* action_manager =
- extensions::ExtensionActionManager::Get(browser_->profile());
- const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
- for (const scoped_refptr<const extensions::Extension>& extension :
- toolbar_items) {
- toolbar_actions_.push_back(new ExtensionActionViewController(
- extension.get(),
- browser_,
- action_manager->GetExtensionAction(*extension),
- this));
- }
-
- // Component actions come second, and are suppressed if the extension
- // actions are being highlighted.
+ // Get the toolbar actions.
+ toolbar_actions_ = model_->CreateActions(browser_, this);
if (!model_->is_highlighting()) {
// TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
// is fixed.
tracked_objects::ScopedTracker tracking_profile2(
FROM_HERE_WITH_EXPLICIT_FUNCTION(
"ToolbarActionsBar::CreateActions2"));
-
- ScopedVector<ToolbarActionViewController> component_actions =
- ComponentToolbarActionsFactory::GetInstance()->
- GetComponentToolbarActions(browser_);
- DCHECK(component_actions.empty() ||
- extensions::FeatureSwitch::extension_action_redesign()->IsEnabled());
- toolbar_actions_.insert(toolbar_actions_.end(),
- component_actions.begin(),
- component_actions.end());
- component_actions.weak_clear();
}
if (!toolbar_actions_.empty()) {
@@ -462,8 +427,8 @@ void ToolbarActionsBar::OnDragDrop(int dragged_index,
delta = -1;
else if (drag_type == DRAG_TO_MAIN)
delta = 1;
- model_->MoveExtensionIcon(toolbar_actions_[dragged_index]->GetId(),
- dropped_index);
+ model_->MoveActionIcon(toolbar_actions_[dragged_index]->GetId(),
+ dropped_index);
if (delta)
model_->SetVisibleIconCount(model_->visible_icon_count() + delta);
}
@@ -552,7 +517,7 @@ void ToolbarActionsBar::MaybeShowExtensionBubble(
// so wait until animation stops.
pending_extension_bubble_controller_ = controller.Pass();
} else {
- const extensions::ExtensionIdList& affected_extensions =
+ const std::vector<std::string>& affected_extensions =
controller->GetExtensionIdList();
ToolbarActionViewController* anchor_action = nullptr;
for (const std::string& id : affected_extensions) {
@@ -564,11 +529,20 @@ void ToolbarActionsBar::MaybeShowExtensionBubble(
}
}
-void ToolbarActionsBar::OnToolbarExtensionAdded(
- const extensions::Extension* extension,
- int index) {
- DCHECK(GetActionForId(extension->id()) == nullptr) <<
- "Asked to add a toolbar action view for an extension that already exists";
+void ToolbarActionsBar::OnToolbarActionAdded(const std::string& action_id,
+ int index) {
+ DCHECK(GetActionForId(action_id) == nullptr)
+ << "Asked to add a toolbar action view for an extension that already "
+ "exists";
+
+ // TODO(devlin): This is a minor layering violation and the model should pass
+ // in an action directly.
+ const extensions::Extension* extension =
+ extensions::ExtensionRegistry::Get(browser_->profile())
+ ->enabled_extensions()
+ .GetByID(action_id);
+ // Only extensions should be added after initialization.
+ DCHECK(extension);
toolbar_actions_.insert(
toolbar_actions_.begin() + index,
@@ -582,7 +556,7 @@ void ToolbarActionsBar::OnToolbarExtensionAdded(
delegate_->AddViewForAction(toolbar_actions_[index], index);
// If we are still initializing the container, don't bother animating.
- if (!model_->extensions_initialized())
+ if (!model_->actions_initialized())
return;
// We may need to resize (e.g. to show the new icon, or the chevron). We don't
@@ -596,10 +570,9 @@ void ToolbarActionsBar::OnToolbarExtensionAdded(
ResizeDelegate(gfx::Tween::LINEAR, true);
}
-void ToolbarActionsBar::OnToolbarExtensionRemoved(
- const extensions::Extension* extension) {
+void ToolbarActionsBar::OnToolbarActionRemoved(const std::string& action_id) {
ToolbarActions::iterator iter = toolbar_actions_.begin();
- while (iter != toolbar_actions_.end() && (*iter)->GetId() != extension->id())
+ while (iter != toolbar_actions_.end() && (*iter)->GetId() != action_id)
++iter;
if (iter == toolbar_actions_.end())
@@ -617,11 +590,11 @@ void ToolbarActionsBar::OnToolbarExtensionRemoved(
// because the icon is just going to get re-added to the same location.
// There is an exception if this is an off-the-record profile, and the
// extension is no longer incognito-enabled.
- if (!extensions::ExtensionSystem::Get(browser_->profile())->runtime_data()->
- IsBeingUpgraded(extension->id()) ||
+ if (!extensions::ExtensionSystem::Get(browser_->profile())
+ ->runtime_data()
+ ->IsBeingUpgraded(action_id) ||
(browser_->profile()->IsOffTheRecord() &&
- !extensions::util::IsIncognitoEnabled(extension->id(),
- browser_->profile()))) {
+ !extensions::util::IsIncognitoEnabled(action_id, browser_->profile()))) {
if (toolbar_actions_.size() > model_->visible_icon_count()) {
// If we have more icons than we can show, then we must not be changing
// the container size (since we either removed an icon from the main
@@ -639,9 +612,8 @@ void ToolbarActionsBar::OnToolbarExtensionRemoved(
SetOverflowedActionWantsToRun();
}
-void ToolbarActionsBar::OnToolbarExtensionMoved(
- const extensions::Extension* extension,
- int index) {
+void ToolbarActionsBar::OnToolbarActionMoved(const std::string& action_id,
+ int index) {
DCHECK(index >= 0 && index < static_cast<int>(toolbar_actions_.size()));
// Unfortunately, |index| doesn't really mean a lot to us, because this
// window's toolbar could be different (if actions are popped out). Just
@@ -649,9 +621,8 @@ void ToolbarActionsBar::OnToolbarExtensionMoved(
ReorderActions();
}
-void ToolbarActionsBar::OnToolbarExtensionUpdated(
- const extensions::Extension* extension) {
- ToolbarActionViewController* action = GetActionForId(extension->id());
+void ToolbarActionsBar::OnToolbarActionUpdated(const std::string& action_id) {
+ ToolbarActionViewController* action = GetActionForId(action_id);
// There might not be a view in cases where we are highlighting or if we
// haven't fully initialized the actions.
if (action) {
@@ -660,14 +631,13 @@ void ToolbarActionsBar::OnToolbarExtensionUpdated(
}
}
-bool ToolbarActionsBar::ShowExtensionActionPopup(
- const extensions::Extension* extension,
- bool grant_active_tab) {
+bool ToolbarActionsBar::ShowToolbarActionPopup(const std::string& action_id,
+ bool grant_active_tab) {
// Don't override another popup, and only show in the active window.
if (popup_owner() || !browser_->window()->IsActive())
return false;
- ToolbarActionViewController* action = GetActionForId(extension->id());
+ ToolbarActionViewController* action = GetActionForId(action_id);
return action && action->ExecuteAction(grant_active_tab);
}
@@ -697,7 +667,7 @@ void ToolbarActionsBar::ResizeDelegate(gfx::Tween::Type tween_type,
}
void ToolbarActionsBar::OnToolbarHighlightModeChanged(bool is_highlighting) {
- if (!model_->extensions_initialized())
+ if (!model_->actions_initialized())
return;
// It's a bit of a pain that we delete and recreate everything here, but given
// everything else going on (the lack of highlight, [n] more extensions
@@ -733,8 +703,8 @@ void ToolbarActionsBar::ReorderActions() {
// First, reset the order to that of the model.
auto compare = [](ToolbarActionViewController* const& action,
- const scoped_refptr<const extensions::Extension>& ext) {
- return action->GetId() == ext->id();
+ const ToolbarActionsModel::ToolbarItem& item) {
+ return action->GetId() == item.id;
};
SortContainer(&toolbar_actions_.get(), model_->toolbar_items(), compare);
@@ -769,9 +739,9 @@ void ToolbarActionsBar::SetOverflowedActionWantsToRun() {
}
ToolbarActionViewController* ToolbarActionsBar::GetActionForId(
- const std::string& id) {
+ const std::string& action_id) {
for (ToolbarActionViewController* action : toolbar_actions_) {
- if (action->GetId() == id)
+ if (action->GetId() == action_id)
return action;
}
return nullptr;
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.h b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
index 149f720..af68242 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
@@ -10,8 +10,8 @@
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/size.h"
@@ -30,15 +30,14 @@ class ToolbarActionViewController;
// A platform-independent version of the container for toolbar actions,
// including extension actions and component actions.
// This class manages the order of the actions, the actions' state, and owns the
-// action controllers, in addition to (for extensions) interfacing with the
-// extension toolbar model. Further, it manages dimensions for the bar,
-// excluding animations.
+// action controllers, in addition to interfacing with the toolbar actions
+// model. Further, it manages dimensions for the bar, excluding animations.
// This can come in two flavors, main and "overflow". The main bar is visible
// next to the omnibox, and the overflow bar is visible inside the chrome
// (fka wrench) menu. The main bar can have only a single row of icons with
// flexible width, whereas the overflow bar has multiple rows of icons with a
// fixed width (the width of the menu).
-class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
+class ToolbarActionsBar : public ToolbarActionsModel::Observer {
public:
// A struct to contain the platform settings.
struct PlatformSettings {
@@ -178,9 +177,9 @@ class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
return suppress_animation_ || disable_animations_for_testing_;
}
bool is_highlighting() const { return model_ && model_->is_highlighting(); }
- extensions::ExtensionToolbarModel::HighlightType highlight_type() const {
+ ToolbarActionsModel::HighlightType highlight_type() const {
return model_ ? model_->highlight_type()
- : extensions::ExtensionToolbarModel::HIGHLIGHT_NONE;
+ : ToolbarActionsModel::HIGHLIGHT_NONE;
}
const PlatformSettings& platform_settings() const {
return platform_settings_;
@@ -206,17 +205,13 @@ class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
private:
using ToolbarActions = ScopedVector<ToolbarActionViewController>;
- // ExtensionToolbarModel::Observer:
- void OnToolbarExtensionAdded(const extensions::Extension* extension,
- int index) override;
- void OnToolbarExtensionRemoved(
- const extensions::Extension* extension) override;
- void OnToolbarExtensionMoved(const extensions::Extension* extension,
- int index) override;
- void OnToolbarExtensionUpdated(
- const extensions::Extension* extension) override;
- bool ShowExtensionActionPopup(const extensions::Extension* extension,
- bool grant_active_tab) override;
+ // ToolbarActionsModel::Observer:
+ void OnToolbarActionAdded(const std::string& action_id, int index) override;
+ void OnToolbarActionRemoved(const std::string& action_id) override;
+ void OnToolbarActionMoved(const std::string& action_id, int index) override;
+ void OnToolbarActionUpdated(const std::string& action_id) override;
+ bool ShowToolbarActionPopup(const std::string& action_id,
+ bool grant_active_tab) override;
void OnToolbarVisibleCountChanged() override;
void OnToolbarHighlightModeChanged(bool is_highlighting) override;
void OnToolbarModelInitialized() override;
@@ -227,7 +222,7 @@ class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
void ResizeDelegate(gfx::Tween::Type tween_type, bool suppress_chevron);
// Returns the action for the given |id|, if one exists.
- ToolbarActionViewController* GetActionForId(const std::string& id);
+ ToolbarActionViewController* GetActionForId(const std::string& action_id);
// Returns the current web contents.
content::WebContents* GetCurrentWebContents();
@@ -251,7 +246,7 @@ class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
Browser* browser_;
// The observed toolbar model.
- extensions::ExtensionToolbarModel* model_;
+ ToolbarActionsModel* model_;
// The controller for the main toolbar actions bar. This will be null if this
// is the main bar.
@@ -267,8 +262,8 @@ class ToolbarActionsBar : public extensions::ExtensionToolbarModel::Observer {
// from toolbar_actions_).
ToolbarActionViewController* popup_owner_;
- ScopedObserver<extensions::ExtensionToolbarModel,
- extensions::ExtensionToolbarModel::Observer> model_observer_;
+ ScopedObserver<ToolbarActionsModel, ToolbarActionsModel::Observer>
+ model_observer_;
// True if we should suppress layout, such as when we are creating or
// adjusting a lot of actions at once.
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
index c1ddfc3..18fab96 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
@@ -320,7 +320,7 @@ TEST_F(ToolbarActionsBarUnitTest, ToolbarActionsReorderOnPrefChange) {
EXPECT_TRUE(VerifyToolbarOrder(expected_names, 3u, 3u));
}
- extensions::ExtensionIdList new_order;
+ std::vector<std::string> new_order;
new_order.push_back(toolbar_actions_bar()->toolbar_actions_unordered()[1]->
GetId());
new_order.push_back(toolbar_actions_bar()->toolbar_actions_unordered()[2]->
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.h b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.h
index dd72905..268245a 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.h
@@ -75,9 +75,7 @@ class ToolbarActionsBarUnitTest : public BrowserWithTestWindowTest {
ToolbarActionsBar* overflow_bar() {
return overflow_browser_action_test_util_->GetToolbarActionsBar();
}
- extensions::ExtensionToolbarModel* toolbar_model() {
- return toolbar_model_;
- }
+ ToolbarActionsModel* toolbar_model() { return toolbar_model_; }
BrowserActionTestUtil* browser_action_test_util() {
return browser_action_test_util_.get();
}
@@ -86,8 +84,8 @@ class ToolbarActionsBarUnitTest : public BrowserWithTestWindowTest {
}
private:
- // The associated ExtensionToolbarModel (owned by the keyed service setup).
- extensions::ExtensionToolbarModel* toolbar_model_;
+ // The associated ToolbarActionsModel (owned by the keyed service setup).
+ ToolbarActionsModel* toolbar_model_;
// A BrowserActionTestUtil object constructed with the associated
// ToolbarActionsBar.
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model.cc b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
new file mode 100644
index 0000000..a9a770f
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
@@ -0,0 +1,844 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/location.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/prefs/pref_service.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_action_manager.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/extensions/extension_action_view_controller.h"
+#include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/pref_names.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/feature_switch.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/one_shot_event.h"
+
+ToolbarActionsModel::ToolbarActionsModel(
+ Profile* profile,
+ extensions::ExtensionPrefs* extension_prefs)
+ : profile_(profile),
+ extension_prefs_(extension_prefs),
+ prefs_(profile_->GetPrefs()),
+ extension_action_api_(extensions::ExtensionActionAPI::Get(profile_)),
+ extension_registry_(extensions::ExtensionRegistry::Get(profile_)),
+ actions_initialized_(false),
+ use_redesign_(extensions::FeatureSwitch::extension_action_redesign()
+ ->IsEnabled()),
+ highlight_type_(HIGHLIGHT_NONE),
+ extension_action_observer_(this),
+ extension_registry_observer_(this),
+ weak_ptr_factory_(this) {
+ extensions::ExtensionSystem::Get(profile_)->ready().Post(
+ FROM_HERE, base::Bind(&ToolbarActionsModel::OnReady,
+ weak_ptr_factory_.GetWeakPtr()));
+ visible_icon_count_ =
+ prefs_->GetInteger(extensions::pref_names::kToolbarSize);
+
+ // We only care about watching the prefs if not in incognito mode.
+ if (!profile_->IsOffTheRecord()) {
+ pref_change_registrar_.Init(prefs_);
+ pref_change_callback_ =
+ base::Bind(&ToolbarActionsModel::OnActionToolbarPrefChange,
+ base::Unretained(this));
+ pref_change_registrar_.Add(extensions::pref_names::kToolbar,
+ pref_change_callback_);
+ }
+}
+
+ToolbarActionsModel::~ToolbarActionsModel() {}
+
+// static
+ToolbarActionsModel* ToolbarActionsModel::Get(Profile* profile) {
+ return ToolbarActionsModelFactory::GetForProfile(profile);
+}
+
+void ToolbarActionsModel::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ToolbarActionsModel::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void ToolbarActionsModel::MoveActionIcon(const std::string& id, size_t index) {
+ std::vector<ToolbarItem>::iterator pos = toolbar_items_.begin();
+ while (pos != toolbar_items_.end() && (*pos).id != id)
+ ++pos;
+ if (pos == toolbar_items_.end()) {
+ NOTREACHED();
+ return;
+ }
+
+ ToolbarItem action = *pos;
+ toolbar_items_.erase(pos);
+
+ std::vector<std::string>::iterator pos_id =
+ std::find(last_known_positions_.begin(), last_known_positions_.end(), id);
+ if (pos_id != last_known_positions_.end())
+ last_known_positions_.erase(pos_id);
+
+ if (index < toolbar_items_.size()) {
+ // If the index is not at the end, find the item currently at |index|, and
+ // insert |action| before it in |toolbar_items_| and |action|'s id in
+ // |last_known_positions_|.
+ std::vector<ToolbarItem>::iterator iter = toolbar_items_.begin() + index;
+ last_known_positions_.insert(
+ std::find(last_known_positions_.begin(), last_known_positions_.end(),
+ iter->id),
+ id);
+ toolbar_items_.insert(iter, action);
+ } else {
+ // Otherwise, put |action| and |id| at the end.
+ DCHECK_EQ(toolbar_items_.size(), index);
+ toolbar_items_.push_back(action);
+ last_known_positions_.push_back(id);
+ }
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnToolbarActionMoved(id, index));
+ MaybeUpdateVisibilityPref(action, index);
+ UpdatePrefs();
+}
+
+void ToolbarActionsModel::SetVisibleIconCount(size_t count) {
+ visible_icon_count_ = (count >= toolbar_items_.size()) ? -1 : count;
+
+ // Only set the prefs if we're not in highlight mode and the profile is not
+ // incognito. Highlight mode is designed to be a transitory state, and should
+ // not persist across browser restarts (though it may be re-entered), and we
+ // don't store anything in incognito.
+ if (!is_highlighting() && !profile_->IsOffTheRecord()) {
+ // Additionally, if we are using the new toolbar, any icons which are in the
+ // overflow menu are considered "hidden". But it so happens that the times
+ // we are likely to call SetVisibleIconCount() are also those when we are
+ // in flux. So wait for things to cool down before setting the prefs.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ToolbarActionsModel::MaybeUpdateVisibilityPrefs,
+ weak_ptr_factory_.GetWeakPtr()));
+ prefs_->SetInteger(extensions::pref_names::kToolbarSize,
+ visible_icon_count_);
+ }
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnToolbarVisibleCountChanged());
+}
+
+void ToolbarActionsModel::OnExtensionActionUpdated(
+ ExtensionAction* extension_action,
+ content::WebContents* web_contents,
+ content::BrowserContext* browser_context) {
+ // Notify observers if the extension exists and is in the model.
+ if (std::find(toolbar_items_.begin(), toolbar_items_.end(),
+ ToolbarItem(extension_action->extension_id(),
+ EXTENSION_ACTION)) != toolbar_items_.end()) {
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarActionUpdated(extension_action->extension_id()));
+ }
+}
+
+ScopedVector<ToolbarActionViewController> ToolbarActionsModel::CreateActions(
+ Browser* browser,
+ ToolbarActionsBar* bar) {
+ DCHECK(browser);
+ DCHECK(bar);
+ ScopedVector<ToolbarActionViewController> action_list;
+
+ // Get the component action list.
+ ScopedVector<ToolbarActionViewController> component_actions =
+ ComponentToolbarActionsFactory::GetInstance()->GetComponentToolbarActions(
+ browser);
+
+ extensions::ExtensionActionManager* action_manager =
+ extensions::ExtensionActionManager::Get(profile_);
+
+ // toolbar_items() might not equate to toolbar_items_ in the case where a
+ // subset are highlighted.
+ std::vector<ToolbarItem> items = toolbar_items();
+ for (const ToolbarItem& action : items) {
+ if (action.type == EXTENSION_ACTION) {
+ // Get the extension.
+ const extensions::Extension* extension = GetExtensionById(action.id);
+ DCHECK(extension);
+
+ // Create and add an ExtensionActionViewController for the extension.
+ action_list.push_back(new ExtensionActionViewController(
+ extension, browser, action_manager->GetExtensionAction(*extension),
+ bar));
+ } else if (action.type == COMPONENT_ACTION) {
+ DCHECK(use_redesign_);
+ // Find the corresponding action to |action|.
+ for (auto component_action : component_actions) {
+ if (component_action->GetId() == action.id) {
+ action_list.push_back(component_action);
+ break;
+ }
+ }
+ }
+ }
+
+ // We've moved ownership of the subset of the component actions that we
+ // kept track of via toolbar_items() from |component_actions| to
+ // |action_list|, so we don't need to keep track of these.
+ component_actions.weak_clear();
+
+ return action_list.Pass();
+}
+
+void ToolbarActionsModel::OnExtensionActionVisibilityChanged(
+ const std::string& extension_id,
+ bool is_now_visible) {
+ // Hiding works differently with the new and old toolbars.
+ if (use_redesign_) {
+ // It's possible that we haven't added this action yet, if its
+ // visibility was adjusted in the course of its initialization.
+ if (std::find(toolbar_items_.begin(), toolbar_items_.end(),
+ ToolbarItem(extension_id, EXTENSION_ACTION)) ==
+ toolbar_items_.end())
+ return;
+
+ int new_size = 0;
+ int new_index = 0;
+ if (is_now_visible) {
+ // If this action used to be hidden, we can't possibly be showing all.
+ DCHECK_LT(visible_icon_count(), toolbar_items_.size());
+ // Grow the bar by one and move the action to the end of the visibles.
+ new_size = visible_icon_count() + 1;
+ new_index = new_size - 1;
+ } else {
+ // If we're hiding one, we must be showing at least one.
+ DCHECK_GE(visible_icon_count(), 0u);
+ // Shrink the bar by one and move the action to the beginning of the
+ // overflow menu.
+ new_size = visible_icon_count() - 1;
+ new_index = new_size;
+ }
+ SetVisibleIconCount(new_size);
+ MoveActionIcon(extension_id, new_index);
+ } else { // Don't include all extensions.
+ const extensions::Extension* extension = GetExtensionById(extension_id);
+ if (is_now_visible)
+ AddExtension(extension);
+ else
+ RemoveExtension(extension);
+ }
+}
+
+void ToolbarActionsModel::OnExtensionLoaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension) {
+ // We don't want to add the same extension twice. It may have already been
+ // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
+ // hides the browser action and then disables and enables the extension.
+ if (std::find(toolbar_items_.begin(), toolbar_items_.end(),
+ ToolbarItem(extension->id(), EXTENSION_ACTION)) !=
+ toolbar_items_.end())
+ return;
+
+ AddExtension(extension);
+}
+
+void ToolbarActionsModel::OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionInfo::Reason reason) {
+ RemoveExtension(extension);
+}
+
+void ToolbarActionsModel::OnExtensionUninstalled(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UninstallReason reason) {
+ // Remove the extension id from the ordered list, if it exists (the extension
+ // might not be represented in the list because it might not have an icon).
+ std::vector<std::string>::iterator pos =
+ std::find(last_known_positions_.begin(), last_known_positions_.end(),
+ extension->id());
+
+ if (pos != last_known_positions_.end()) {
+ last_known_positions_.erase(pos);
+ UpdatePrefs();
+ }
+}
+
+void ToolbarActionsModel::OnReady() {
+ InitializeActionList();
+ // Wait until the extension system is ready before observing any further
+ // changes so that the toolbar buttons can be shown in their stable ordering
+ // taken from prefs.
+ extension_registry_observer_.Add(extension_registry_);
+ extension_action_observer_.Add(extension_action_api_);
+
+ if (ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
+ profile_)) {
+ std::vector<std::string> ids;
+ for (const ToolbarItem& action : toolbar_items_)
+ ids.push_back(action.id);
+ HighlightActions(ids, HIGHLIGHT_INFO);
+ }
+
+ actions_initialized_ = true;
+ FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
+}
+
+size_t ToolbarActionsModel::FindNewPositionFromLastKnownGood(
+ const ToolbarItem& action) {
+ // See if we have last known good position for this action.
+ size_t new_index = 0;
+ // Loop through the ID list of known positions, to count the number of
+ // visible action icons preceding |action|'s id.
+ for (const std::string& last_pos_id : last_known_positions_) {
+ if (last_pos_id == action.id)
+ return new_index; // We've found the right position.
+ // Found an action, need to see if it is visible.
+ for (const ToolbarItem& item : toolbar_items_) {
+ if (item.id == last_pos_id) {
+ // This extension is visible, update the index value.
+ ++new_index;
+ break;
+ }
+ }
+ }
+
+ // Position not found.
+ return toolbar_items_.size();
+}
+
+bool ToolbarActionsModel::ShouldAddExtension(
+ const extensions::Extension* extension) {
+ // In incognito mode, don't add any extensions that aren't incognito-enabled.
+ if (profile_->IsOffTheRecord() &&
+ !extensions::util::IsIncognitoEnabled(extension->id(), profile_))
+ return false;
+
+ extensions::ExtensionActionManager* action_manager =
+ extensions::ExtensionActionManager::Get(profile_);
+ if (use_redesign_) {
+ // In this case, we don't care about the browser action visibility, because
+ // we want to show each extension regardless.
+ return action_manager->GetExtensionAction(*extension) != NULL;
+ }
+
+ return action_manager->GetBrowserAction(*extension) &&
+ extension_action_api_->GetBrowserActionVisibility(extension->id());
+}
+
+void ToolbarActionsModel::AddExtension(const extensions::Extension* extension) {
+ // We only use AddExtension() once the system is initialized.
+ DCHECK(actions_initialized_);
+ if (!ShouldAddExtension(extension))
+ return;
+
+ // See if we have a last known good position for this extension.
+ bool is_new_extension =
+ std::find(last_known_positions_.begin(), last_known_positions_.end(),
+ extension->id()) == last_known_positions_.end();
+
+ // New extensions go at the right (end) of the visible extensions. Other
+ // extensions go at their previous position.
+ size_t new_index = 0;
+ if (is_new_extension) {
+ new_index = extensions::Manifest::IsComponentLocation(extension->location())
+ ? 0
+ : visible_icon_count();
+ // For the last-known position, we use the index of the extension that is
+ // just before this extension, plus one. (Note that this isn't the same
+ // as new_index + 1, because last_known_positions_ can include disabled
+ // extensions.)
+ int new_last_known_index =
+ new_index == 0 ? 0 : std::find(last_known_positions_.begin(),
+ last_known_positions_.end(),
+ toolbar_items_[new_index - 1].id) -
+ last_known_positions_.begin() + 1;
+ // In theory, the extension before this one should always
+ // be in last known positions, but if something funny happened with prefs,
+ // make sure we handle it.
+ // TODO(devlin): Track down these cases so we can CHECK this.
+ new_last_known_index =
+ std::min<int>(new_last_known_index, last_known_positions_.size());
+ last_known_positions_.insert(
+ last_known_positions_.begin() + new_last_known_index, extension->id());
+ UpdatePrefs();
+ } else {
+ new_index = FindNewPositionFromLastKnownGood(
+ ToolbarItem(extension->id(), EXTENSION_ACTION));
+ }
+
+ toolbar_items_.insert(toolbar_items_.begin() + new_index,
+ ToolbarItem(extension->id(), EXTENSION_ACTION));
+
+ // If we're currently highlighting, then even though we add a browser action
+ // to the full list (|toolbar_items_|, there won't be another *visible*
+ // browser action, which was what the observers care about.
+ if (!is_highlighting()) {
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarActionAdded(extension->id(), new_index));
+
+ int visible_count_delta = 0;
+ if (is_new_extension && !all_icons_visible()) {
+ // If this is a new extension (and not all extensions are visible), we
+ // expand the toolbar out so that the new one can be seen.
+ visible_count_delta = 1;
+ } else if (profile_->IsOffTheRecord()) {
+ // If this is an incognito profile, we also have to check to make sure the
+ // overflow matches the main bar's status.
+ ToolbarActionsModel* main_model =
+ ToolbarActionsModel::Get(profile_->GetOriginalProfile());
+ // Find what the index will be in the main bar. Because Observer calls are
+ // nondeterministic, we can't just assume the main bar will have the
+ // extension and look it up.
+ size_t main_index = main_model->FindNewPositionFromLastKnownGood(
+ ToolbarItem(extension->id(), EXTENSION_ACTION));
+ bool visible = main_index < main_model->visible_icon_count();
+ // We may need to adjust the visible count if the incognito bar isn't
+ // showing all icons and this one is visible, or if it is showing all
+ // icons and this is hidden.
+ if (visible && !all_icons_visible())
+ visible_count_delta = 1;
+ else if (!visible && all_icons_visible())
+ visible_count_delta = -1;
+ }
+
+ if (visible_count_delta)
+ SetVisibleIconCount(visible_icon_count() + visible_count_delta);
+ }
+
+ MaybeUpdateVisibilityPref(ToolbarItem(extension->id(), EXTENSION_ACTION),
+ new_index);
+}
+
+void ToolbarActionsModel::RemoveExtension(
+ const extensions::Extension* extension) {
+ std::vector<ToolbarItem>::iterator pos =
+ std::find(toolbar_items_.begin(), toolbar_items_.end(),
+ ToolbarItem(extension->id(), EXTENSION_ACTION));
+
+ if (pos == toolbar_items_.end())
+ return;
+
+ size_t index = pos - toolbar_items_.begin();
+ // If the removed extension was on the toolbar, a new one will take its place
+ // if there are any in overflow.
+ bool new_extension_shown =
+ !all_icons_visible() && index < visible_icon_count();
+
+ // If our visible count is set to the current size, we need to decrement it.
+ if (visible_icon_count_ == static_cast<int>(toolbar_items_.size()))
+ SetVisibleIconCount(toolbar_items_.size() - 1);
+
+ toolbar_items_.erase(pos);
+
+ // If we're in highlight mode, we also have to remove the extension from
+ // the highlighted list.
+ if (is_highlighting()) {
+ pos = std::find(highlighted_items_.begin(), highlighted_items_.end(),
+ ToolbarItem(extension->id(), EXTENSION_ACTION));
+ if (pos != highlighted_items_.end()) {
+ highlighted_items_.erase(pos);
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarActionRemoved(extension->id()));
+ // If the highlighted list is now empty, we stop highlighting.
+ if (highlighted_items_.empty())
+ StopHighlighting();
+ }
+ } else {
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarActionRemoved(extension->id()));
+ }
+
+ UpdatePrefs();
+ if (new_extension_shown) {
+ size_t newly_visible_index = visible_icon_count() - 1;
+ MaybeUpdateVisibilityPref(toolbar_items_[newly_visible_index],
+ newly_visible_index);
+ }
+}
+
+// Combine the currently enabled extensions that have browser actions (which
+// we get from the ExtensionRegistry) and component actions (which we get from
+// ComponentToolbarActionsFactory) with the ordering we get from the pref
+// service. For robustness we use a somewhat inefficient process:
+// 1. Create a vector of actions sorted by their pref values. This vector may
+// have holes.
+// 2. Create a vector of actions that did not have a pref value.
+// 3. Remove holes from the sorted vector and append the unsorted vector.
+void ToolbarActionsModel::InitializeActionList() {
+ DCHECK(toolbar_items_.empty()); // We shouldn't have any items yet.
+
+ last_known_positions_ = extension_prefs_->GetToolbarOrder();
+ if (profile_->IsOffTheRecord())
+ IncognitoPopulate();
+ else
+ Populate(&last_known_positions_);
+
+ MaybeUpdateVisibilityPrefs();
+}
+
+void ToolbarActionsModel::Populate(std::vector<std::string>* positions) {
+ DCHECK(!profile_->IsOffTheRecord());
+
+ std::vector<ToolbarItem> all_actions;
+ // Ids of actions that have explicit positions.
+ std::vector<ToolbarItem> sorted(positions->size(), ToolbarItem());
+ // Ids of actions that don't have explicit positions.
+ std::vector<ToolbarItem> unsorted;
+
+ // Populate the lists.
+ int hidden = 0;
+ int browser_actions_count = 0;
+ int component_actions_count = 0;
+
+ // First, add the extension action ids to all_actions.
+ const extensions::ExtensionSet& extensions =
+ extension_registry_->enabled_extensions();
+ for (const scoped_refptr<const extensions::Extension>& extension :
+ extensions) {
+ if (!ShouldAddExtension(extension.get())) {
+ if (!extension_action_api_->GetBrowserActionVisibility(extension->id()))
+ ++hidden;
+ continue;
+ }
+
+ all_actions.push_back(ToolbarItem(extension->id(), EXTENSION_ACTION));
+ }
+
+ // Next, add the component action ids.
+ std::vector<std::string> component_ids =
+ ComponentToolbarActionsFactory::GetComponentIds();
+ for (const std::string& id : component_ids)
+ all_actions.push_back(ToolbarItem(id, COMPONENT_ACTION));
+
+ // Add each action id to the appropriate list.
+ for (const ToolbarItem& action : all_actions) {
+ std::vector<std::string>::const_iterator pos =
+ std::find(positions->begin(), positions->end(), action.id);
+ if (pos != positions->end()) {
+ sorted[pos - positions->begin()] = action;
+ } else {
+ // Unknown action - push it to the back of unsorted, and add it to the
+ // list of ids at the end.
+ unsorted.push_back(action);
+ positions->push_back(action.id);
+ }
+ }
+
+ // Merge the lists.
+ sorted.insert(sorted.end(), unsorted.begin(), unsorted.end());
+ toolbar_items_.reserve(sorted.size());
+
+ // We don't notify observers of the added extension yet. Rather, observers
+ // should wait for the "OnToolbarModelInitialized" notification, and then
+ // bulk-update. (This saves a lot of bouncing-back-and-forth here, and allows
+ // observers to ensure that the extension system is always initialized before
+ // using the extensions).
+ for (const ToolbarItem& action : sorted) {
+ if (action.type == EXTENSION_ACTION) {
+ // It's possible for the extension order to contain items that aren't
+ // actually loaded on this machine. For example, when extension sync is
+ // on, we sync the extension order as-is but double-check with the user
+ // before syncing NPAPI-containing extensions, so if one of those is not
+ // actually synced, we'll get a NULL in the list. This sort of case can
+ // also happen if some error prevents an extension from loading.
+ if (GetExtensionById(action.id)) {
+ toolbar_items_.push_back(ToolbarItem(action.id, EXTENSION_ACTION));
+ ++browser_actions_count;
+ }
+ } else if (action.type == COMPONENT_ACTION) {
+ toolbar_items_.push_back(ToolbarItem(action.id, COMPONENT_ACTION));
+ ++component_actions_count;
+ }
+ }
+
+ // Histogram names are prefixed with "ExtensionToolbarModel" rather than
+ // "ToolbarActionsModel" for historical reasons.
+ UMA_HISTOGRAM_COUNTS_100(
+ "ExtensionToolbarModel.BrowserActionsPermanentlyHidden", hidden);
+ UMA_HISTOGRAM_COUNTS_100("ExtensionToolbarModel.BrowserActionsCount",
+ browser_actions_count);
+ UMA_HISTOGRAM_COUNTS_100("Toolbar.ActionsModel.ComponentActionsCount",
+ component_actions_count);
+ UMA_HISTOGRAM_COUNTS_100("Toolbar.ActionsModel.OverallActionsCount",
+ toolbar_items_.size());
+
+ if (!toolbar_items_.empty()) {
+ // Visible count can be -1, meaning: 'show all'. Since UMA converts negative
+ // values to 0, this would be counted as 'show none' unless we convert it to
+ // max.
+ UMA_HISTOGRAM_COUNTS_100(
+ "ExtensionToolbarModel.BrowserActionsVisible",
+ visible_icon_count_ == -1
+ ? visible_icon_count_
+ : visible_icon_count_ - component_actions_count);
+
+ if (use_redesign_) {
+ // The only time this will useful and possibly vary from
+ // BrowserActionsVisible is when the redesign has been enabled.
+ UMA_HISTOGRAM_COUNTS_100("Toolbar.ActionsModel.ToolbarActionsVisible",
+ visible_icon_count_ == -1
+ ? base::HistogramBase::kSampleType_MAX
+ : visible_icon_count_);
+ }
+ }
+}
+
+void ToolbarActionsModel::IncognitoPopulate() {
+ DCHECK(profile_->IsOffTheRecord());
+ const ToolbarActionsModel* original_model =
+ ToolbarActionsModel::Get(profile_->GetOriginalProfile());
+
+ // Find the absolute value of the original model's count.
+ int original_visible = original_model->visible_icon_count();
+
+ // In incognito mode, we show only those actions that are incognito-enabled
+ // Further, any actions that were overflowed in regular mode are still
+ // overflowed. Order is the same as in regular mode.
+ visible_icon_count_ = 0;
+
+ for (std::vector<ToolbarItem>::const_iterator iter =
+ original_model->toolbar_items_.begin();
+ iter != original_model->toolbar_items_.end(); ++iter) {
+ // The extension might not be shown in incognito mode. For now, all
+ // component actions are present.
+ if (iter->type == EXTENSION_ACTION &&
+ !ShouldAddExtension(GetExtensionById(iter->id)))
+ continue;
+ toolbar_items_.push_back(*iter);
+ if (iter - original_model->toolbar_items_.begin() < original_visible)
+ ++visible_icon_count_;
+ }
+}
+
+void ToolbarActionsModel::UpdatePrefs() {
+ if (!extension_prefs_ || profile_->IsOffTheRecord())
+ return;
+
+ // Don't observe change caused by self.
+ pref_change_registrar_.Remove(extensions::pref_names::kToolbar);
+ extension_prefs_->SetToolbarOrder(last_known_positions_);
+ pref_change_registrar_.Add(extensions::pref_names::kToolbar,
+ pref_change_callback_);
+}
+
+void ToolbarActionsModel::MaybeUpdateVisibilityPref(const ToolbarItem& action,
+ size_t index) {
+ // Component actions don't have prefs to update.
+ if (action.type == COMPONENT_ACTION)
+ return;
+
+ // We only update the visibility pref for hidden/not hidden based on the
+ // overflow menu with the new toolbar design.
+ if (use_redesign_ && !profile_->IsOffTheRecord()) {
+ bool visible = index < visible_icon_count();
+ if (visible !=
+ extension_action_api_->GetBrowserActionVisibility(action.id)) {
+ // Don't observe changes caused by ourselves.
+ bool was_registered = false;
+ if (extension_action_observer_.IsObserving(extension_action_api_)) {
+ was_registered = true;
+ extension_action_observer_.RemoveAll();
+ }
+ extension_action_api_->SetBrowserActionVisibility(action.id, visible);
+ if (was_registered)
+ extension_action_observer_.Add(extension_action_api_);
+ }
+ }
+}
+
+void ToolbarActionsModel::MaybeUpdateVisibilityPrefs() {
+ for (size_t i = 0u; i < toolbar_items_.size(); ++i)
+ MaybeUpdateVisibilityPref(toolbar_items_[i], i);
+}
+
+void ToolbarActionsModel::OnActionToolbarPrefChange() {
+ // If extensions are not ready, defer to later Populate() call.
+ if (!actions_initialized_)
+ return;
+
+ // Recalculate |last_known_positions_| to be |pref_positions| followed by
+ // ones that are only in |last_known_positions_|.
+ std::vector<std::string> pref_positions = extension_prefs_->GetToolbarOrder();
+ size_t pref_position_size = pref_positions.size();
+ for (size_t i = 0; i < last_known_positions_.size(); ++i) {
+ if (std::find(pref_positions.begin(), pref_positions.end(),
+ last_known_positions_[i]) == pref_positions.end()) {
+ pref_positions.push_back(last_known_positions_[i]);
+ }
+ }
+ last_known_positions_.swap(pref_positions);
+
+ int desired_index = 0;
+ // Loop over the updated list of last known positions, moving any extensions
+ // that are in the wrong place.
+ for (const std::string& id : last_known_positions_) {
+ int current_index = GetIndexForId(id);
+ if (current_index == -1)
+ continue;
+ if (current_index != desired_index) {
+ ToolbarItem action = toolbar_items_[current_index];
+ toolbar_items_.erase(toolbar_items_.begin() + current_index);
+ toolbar_items_.insert(toolbar_items_.begin() + desired_index, action);
+ // Notify the observers to keep them up-to-date.
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarActionMoved(action.id, desired_index));
+ }
+ ++desired_index;
+ }
+
+ if (last_known_positions_.size() > pref_position_size) {
+ // Need to update pref because we have extra icons. But can't call
+ // UpdatePrefs() directly within observation closure.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&ToolbarActionsModel::UpdatePrefs,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+}
+
+int ToolbarActionsModel::GetIndexForId(const std::string& id) const {
+ for (size_t i = 0; i < toolbar_items().size(); ++i) {
+ if (toolbar_items()[i].id == id)
+ return i;
+ }
+ return -1;
+}
+
+bool ToolbarActionsModel::ShowToolbarActionPopup(const std::string& id,
+ Browser* browser,
+ bool grant_active_tab) {
+ base::ObserverListBase<Observer>::Iterator it(&observers_);
+ Observer* obs = NULL;
+ // Look for the Observer associated with the browser.
+ // This would be cleaner if we had an abstract class for the Toolbar UI
+ // (like we do for LocationBar), but sadly, we don't.
+ while ((obs = it.GetNext()) != NULL) {
+ if (obs->GetBrowser() == browser)
+ return obs->ShowToolbarActionPopup(id, grant_active_tab);
+ }
+ return false;
+}
+
+void ToolbarActionsModel::EnsureVisibility(
+ const std::vector<std::string>& ids) {
+ if (all_icons_visible())
+ return; // Already showing all.
+
+ // Otherwise, make sure we have enough room to show all the extensions
+ // requested.
+ if (visible_icon_count() < ids.size())
+ SetVisibleIconCount(ids.size());
+
+ if (all_icons_visible())
+ return; // May have been set to max by SetVisibleIconCount.
+
+ // Guillotine's Delight: Move an orange noble to the front of the line.
+ for (std::vector<std::string>::const_iterator it = ids.begin();
+ it != ids.end(); ++it) {
+ for (std::vector<ToolbarItem>::const_iterator item = toolbar_items_.begin();
+ item != toolbar_items_.end(); ++item) {
+ if (item->id == *it) {
+ if (item - toolbar_items_.begin() >=
+ static_cast<int>(visible_icon_count()))
+ MoveActionIcon(*it, 0);
+ break;
+ }
+ }
+ }
+}
+
+bool ToolbarActionsModel::HighlightActions(const std::vector<std::string>& ids,
+ HighlightType highlight_type) {
+ highlighted_items_.clear();
+
+ for (const std::string& action_id : ids) {
+ for (const ToolbarItem& item : toolbar_items_) {
+ if (action_id == item.id)
+ highlighted_items_.push_back(item);
+ }
+ }
+
+ // If we have any items in |highlighted_items_|, then we entered highlighting
+ // mode.
+ if (highlighted_items_.size()) {
+ // It's important that is_highlighting_ is changed immediately before the
+ // observers are notified since it changes the result of toolbar_items().
+ highlight_type_ = highlight_type;
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarHighlightModeChanged(true));
+
+ // We set the visible icon count after the highlight mode change because
+ // the UI actions are created/destroyed during highlight, and doing that
+ // prior to changing the size allows us to still have smooth animations.
+ if (visible_icon_count() < ids.size())
+ SetVisibleIconCount(ids.size());
+
+ return true;
+ }
+
+ // Otherwise, we didn't enter highlighting mode (and, in fact, exited it if
+ // we were otherwise in it).
+ if (is_highlighting())
+ StopHighlighting();
+ return false;
+}
+
+void ToolbarActionsModel::StopHighlighting() {
+ if (is_highlighting()) {
+ // It's important that is_highlighting_ is changed immediately before the
+ // observers are notified since it changes the result of toolbar_items().
+ highlight_type_ = HIGHLIGHT_NONE;
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnToolbarHighlightModeChanged(false));
+
+ // For the same reason, we don't clear highlighted_items_ until after the
+ // mode changed.
+ highlighted_items_.clear();
+
+ // We set the visible icon count after the highlight mode change because
+ // the UI actions are created/destroyed during highlight, and doing that
+ // prior to changing the size allows us to still have smooth animations.
+ int saved_icon_count =
+ prefs_->GetInteger(extensions::pref_names::kToolbarSize);
+ if (saved_icon_count != visible_icon_count_)
+ SetVisibleIconCount(saved_icon_count);
+ }
+}
+
+bool ToolbarActionsModel::RedesignIsShowingNewIcons() const {
+ for (const ToolbarItem& action : toolbar_items_) {
+ if (action.type == EXTENSION_ACTION) {
+ // Without the redesign, we only show extensions with browser actions.
+ // Any extension without a browser action is an indication that we're
+ // showing something new.
+ if (!GetExtensionById(action.id)->manifest()->HasKey(
+ extensions::manifest_keys::kBrowserAction))
+ return true;
+ }
+ }
+ return false;
+}
+
+const extensions::Extension* ToolbarActionsModel::GetExtensionById(
+ const std::string& id) const {
+ return extension_registry_->enabled_extensions().GetByID(id);
+}
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model.h b/chrome/browser/ui/toolbar/toolbar_actions_model.h
new file mode 100644
index 0000000..d397f29
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model.h
@@ -0,0 +1,311 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_
+#define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_
+
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/extensions/extension_action.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/common/extension.h"
+
+class Browser;
+class PrefService;
+class Profile;
+class ToolbarActionsBar;
+class ToolbarActionViewController;
+
+namespace extensions {
+class ExtensionRegistry;
+class ExtensionSet;
+}
+
+// Model for the browser actions toolbar.
+class ToolbarActionsModel : public extensions::ExtensionActionAPI::Observer,
+ public extensions::ExtensionRegistryObserver,
+ public KeyedService {
+ public:
+ // The different options for highlighting.
+ enum HighlightType {
+ HIGHLIGHT_NONE,
+ HIGHLIGHT_INFO,
+ HIGHLIGHT_WARNING,
+ };
+
+ // The different types of actions.
+ enum ActionType {
+ COMPONENT_ACTION,
+ EXTENSION_ACTION,
+ };
+
+ // An action id and its corresponding ActionType.
+ struct ToolbarItem {
+ ToolbarItem() {}
+ ToolbarItem(std::string action_id, ActionType action_type)
+ : id(action_id), type(action_type) {}
+
+ bool operator==(const ToolbarItem& other) { return other.id == id; }
+
+ std::string id;
+ ActionType type;
+ };
+
+ ToolbarActionsModel(Profile* profile,
+ extensions::ExtensionPrefs* extension_prefs);
+ ~ToolbarActionsModel() override;
+
+ // A class which is informed of changes to the model; represents the view of
+ // MVC. Also used for signaling view changes such as showing extension popups.
+ // TODO(devlin): Should this really be an observer? It acts more like a
+ // delegate.
+ class Observer {
+ public:
+ // Signals that an action with |id| has been added to the toolbar at
+ // |index|. This will *only* be called after the toolbar model has been
+ // initialized.
+ virtual void OnToolbarActionAdded(const std::string& id, int index) = 0;
+
+ // Signals that the given action with |id| has been removed from the
+ // toolbar.
+ virtual void OnToolbarActionRemoved(const std::string& id) = 0;
+
+ // Signals that the given action with |id| has been moved to |index|.
+ // |index| is the desired *final* index of the action (that is, in the
+ // adjusted order, action should be at |index|).
+ virtual void OnToolbarActionMoved(const std::string& id, int index) = 0;
+
+ // Signals that the browser action with |id| has been updated.
+ virtual void OnToolbarActionUpdated(const std::string& id) = 0;
+
+ // Signals the action with |id| to show the popup now in the active
+ // window. If |grant_active_tab| is true, then active tab permissions
+ // should be given to the action (only do this if this is through a user
+ // action). Returns true if a popup was slated to be shown.
+ virtual bool ShowToolbarActionPopup(const std::string& id,
+ bool grant_active_tab) = 0;
+
+ // Signals when the container needs to be redrawn because of a size change,
+ // and when the model has finished loading.
+ virtual void OnToolbarVisibleCountChanged() = 0;
+
+ // Signals that the model has entered or exited highlighting mode, or that
+ // the actions being highlighted have (probably*) changed. Highlighting
+ // mode indicates that only a subset of the toolbar actions are actively
+ // displayed, and those actions should be highlighted for extra emphasis.
+ // * probably, because if we are in highlight mode and receive a call to
+ // highlight a new set of actions, we do not compare the current set with
+ // the new set (and just assume the new set is different).
+ virtual void OnToolbarHighlightModeChanged(bool is_highlighting) = 0;
+
+ // Signals that the toolbar model has been initialized, so that if any
+ // observers were postponing animation during the initialization stage, they
+ // can catch up.
+ virtual void OnToolbarModelInitialized() = 0;
+
+ // Returns the browser associated with the Observer.
+ virtual Browser* GetBrowser() = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ // Convenience function to get the ToolbarActionsModel for a Profile.
+ static ToolbarActionsModel* Get(Profile* profile);
+
+ // Adds or removes an observer.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Moves the given action with |id|'s icon to the given |index|.
+ void MoveActionIcon(const std::string& id, size_t index);
+
+ // Sets the number of action icons that should be visible.
+ // If count == size(), this will set the visible icon count to -1, meaning
+ // "show all actions".
+ void SetVisibleIconCount(size_t count);
+
+ size_t visible_icon_count() const {
+ // We have guards around this because |visible_icon_count_| can be set by
+ // prefs/sync, and we want to ensure that the icon count returned is within
+ // bounds.
+ return visible_icon_count_ == -1
+ ? toolbar_items().size()
+ : std::min(static_cast<size_t>(visible_icon_count_),
+ toolbar_items().size());
+ }
+
+ bool all_icons_visible() const { return visible_icon_count_ == -1; }
+
+ bool actions_initialized() const { return actions_initialized_; }
+
+ ScopedVector<ToolbarActionViewController> CreateActions(
+ Browser* browser,
+ ToolbarActionsBar* bar);
+
+ const std::vector<ToolbarItem>& toolbar_items() const {
+ return is_highlighting() ? highlighted_items_ : toolbar_items_;
+ }
+
+ bool is_highlighting() const { return highlight_type_ != HIGHLIGHT_NONE; }
+ HighlightType highlight_type() const { return highlight_type_; }
+
+ void OnActionToolbarPrefChange();
+
+ // Returns the index of the given action with |id|, or -1 if the id
+ // wasn't found.
+ int GetIndexForId(const std::string& id) const;
+
+ // Finds the Observer associated with |browser| and tells it to display a
+ // popup for the given action with |id|. If |grant_active_tab| is true,
+ // this grants active tab permissions to the action; only do this because of
+ // a direct user action.
+ bool ShowToolbarActionPopup(const std::string& id,
+ Browser* browser,
+ bool grant_active_tab);
+
+ // Ensures that the actions in the |action_ids| list are visible on the
+ // toolbar. This might mean they need to be moved to the front (if they are in
+ // the overflow bucket).
+ void EnsureVisibility(const std::vector<std::string>& action_ids);
+
+ // Highlights the actions specified by |action_ids|. This will cause
+ // the ToolbarModel to only display those actions.
+ // Highlighting mode is only entered if there is at least one action to be
+ // shown.
+ // Returns true if highlighting mode is entered, false otherwise.
+ bool HighlightActions(const std::vector<std::string>& action_ids,
+ HighlightType type);
+
+ // Stop highlighting actions. All actions can be shown again, and the
+ // number of visible icons will be reset to what it was before highlighting.
+ void StopHighlighting();
+
+ // Returns true if the toolbar model is running with the redesign and is
+ // showing new icons as a result.
+ bool RedesignIsShowingNewIcons() const;
+
+ private:
+ // Callback when actions are ready.
+ void OnReady();
+
+ // ExtensionRegistryObserver:
+ void OnExtensionLoaded(content::BrowserContext* browser_context,
+ const extensions::Extension* extension) override;
+ void OnExtensionUnloaded(
+ content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UnloadedExtensionInfo::Reason reason) override;
+ void OnExtensionUninstalled(content::BrowserContext* browser_context,
+ const extensions::Extension* extension,
+ extensions::UninstallReason reason) override;
+
+ // ExtensionActionAPI::Observer:
+ void OnExtensionActionUpdated(
+ ExtensionAction* extension_action,
+ content::WebContents* web_contents,
+ content::BrowserContext* browser_context) override;
+ void OnExtensionActionVisibilityChanged(const std::string& extension_id,
+ bool is_now_visible) override;
+
+ // To be called after the extension service is ready; gets loaded extensions
+ // from the ExtensionRegistry and their saved order from the pref service
+ // and constructs |toolbar_items_| from these data. IncognitoPopulate()
+ // takes the shortcut - looking at the regular model's content and modifying
+ // it.
+ void InitializeActionList();
+ void Populate(std::vector<std::string>* positions);
+ void IncognitoPopulate();
+
+ // Save the model to prefs.
+ void UpdatePrefs();
+
+ // Updates action with |action|'s id's browser action visibility pref if the
+ // browser action is in the overflow menu and should be considered hidden.
+ void MaybeUpdateVisibilityPref(const ToolbarItem& action, size_t index);
+
+ // Calls MaybeUpdateVisibilityPref() for each action in |toolbar_items|.
+ void MaybeUpdateVisibilityPrefs();
+
+ // Finds the last known visible position of the icon for |action|. The value
+ // returned is a zero-based index into the vector of visible items.
+ size_t FindNewPositionFromLastKnownGood(const ToolbarItem& action);
+
+ // Returns true if the given |extension| should be added to the toolbar.
+ bool ShouldAddExtension(const extensions::Extension* extension);
+
+ // Adds or removes the given |extension| from the toolbar model.
+ void AddExtension(const extensions::Extension* extension);
+ void RemoveExtension(const extensions::Extension* extension);
+
+ // Looks up and returns the extension with the given |id| in the set of
+ // enabled extensions.
+ const extensions::Extension* GetExtensionById(const std::string& id) const;
+
+ // Our observers.
+ base::ObserverList<Observer> observers_;
+
+ // The Profile this toolbar model is for.
+ Profile* profile_;
+
+ extensions::ExtensionPrefs* extension_prefs_;
+ PrefService* prefs_;
+
+ // The ExtensionActionAPI object, cached for convenience.
+ extensions::ExtensionActionAPI* extension_action_api_;
+
+ // The ExtensionRegistry object, cached for convenience.
+ extensions::ExtensionRegistry* extension_registry_;
+
+ // True if we've handled the initial EXTENSIONS_READY notification.
+ bool actions_initialized_;
+
+ // If true, we include all actions in the toolbar model.
+ bool use_redesign_;
+
+ // Ordered list of browser actions.
+ std::vector<ToolbarItem> toolbar_items_;
+
+ // List of browser actions which should be highlighted.
+ std::vector<ToolbarItem> highlighted_items_;
+
+ // The current type of highlight (with HIGHLIGHT_NONE indicating no current
+ // highlight).
+ HighlightType highlight_type_;
+
+ // A list of action ids ordered to correspond with their last known
+ // positions.
+ std::vector<std::string> last_known_positions_;
+
+ // The number of icons visible (the rest should be hidden in the overflow
+ // chevron). A value of -1 indicates that all icons should be visible.
+ // Instead of using this variable directly, use visible_icon_count() if
+ // possible.
+ // TODO(devlin): Make a new variable to indicate that all icons should be
+ // visible, instead of overloading this one.
+ int visible_icon_count_;
+
+ ScopedObserver<extensions::ExtensionActionAPI,
+ extensions::ExtensionActionAPI::Observer>
+ extension_action_observer_;
+
+ // Listen to extension load, unloaded notifications.
+ ScopedObserver<extensions::ExtensionRegistry, ExtensionRegistryObserver>
+ extension_registry_observer_;
+
+ // For observing change of toolbar order preference by external entity (sync).
+ PrefChangeRegistrar pref_change_registrar_;
+ base::Closure pref_change_callback_;
+
+ base::WeakPtrFactory<ToolbarActionsModel> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToolbarActionsModel);
+};
+
+#endif // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_H_
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_factory.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_factory.cc
new file mode 100644
index 0000000..b6e80ee
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_factory.cc
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
+
+#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_prefs_factory.h"
+#include "extensions/browser/extensions_browser_client.h"
+
+// static
+ToolbarActionsModel* ToolbarActionsModelFactory::GetForProfile(
+ Profile* profile) {
+ return static_cast<ToolbarActionsModel*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+ToolbarActionsModelFactory* ToolbarActionsModelFactory::GetInstance() {
+ return Singleton<ToolbarActionsModelFactory>::get();
+}
+
+ToolbarActionsModelFactory::ToolbarActionsModelFactory()
+ : BrowserContextKeyedServiceFactory(
+ "ToolbarActionsModel",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(extensions::ExtensionPrefsFactory::GetInstance());
+ DependsOn(extensions::ExtensionActionAPI::GetFactoryInstance());
+}
+
+ToolbarActionsModelFactory::~ToolbarActionsModelFactory() {}
+
+KeyedService* ToolbarActionsModelFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ return new ToolbarActionsModel(
+ Profile::FromBrowserContext(context),
+ extensions::ExtensionPrefsFactory::GetForBrowserContext(context));
+}
+
+content::BrowserContext* ToolbarActionsModelFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ return context;
+}
+
+bool ToolbarActionsModelFactory::ServiceIsCreatedWithBrowserContext() const {
+ return true;
+}
+
+bool ToolbarActionsModelFactory::ServiceIsNULLWhileTesting() const {
+ return true;
+}
diff --git a/chrome/browser/extensions/extension_toolbar_model_factory.h b/chrome/browser/ui/toolbar/toolbar_actions_model_factory.h
index 6b5c77f..cc89475 100644
--- a/chrome/browser/extensions/extension_toolbar_model_factory.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_factory.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_FACTORY_H_
-#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_FACTORY_H_
+#ifndef CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_FACTORY_H_
+#define CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_FACTORY_H_
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
@@ -11,21 +11,19 @@
class Profile;
-namespace extensions {
+class ToolbarActionsModel;
-class ExtensionToolbarModel;
-
-class ExtensionToolbarModelFactory : public BrowserContextKeyedServiceFactory {
+class ToolbarActionsModelFactory : public BrowserContextKeyedServiceFactory {
public:
- static ExtensionToolbarModel* GetForProfile(Profile* profile);
+ static ToolbarActionsModel* GetForProfile(Profile* profile);
- static ExtensionToolbarModelFactory* GetInstance();
+ static ToolbarActionsModelFactory* GetInstance();
private:
- friend struct DefaultSingletonTraits<ExtensionToolbarModelFactory>;
+ friend struct DefaultSingletonTraits<ToolbarActionsModelFactory>;
- ExtensionToolbarModelFactory();
- ~ExtensionToolbarModelFactory() override;
+ ToolbarActionsModelFactory();
+ ~ToolbarActionsModelFactory() override;
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* profile) const override;
@@ -35,6 +33,4 @@ class ExtensionToolbarModelFactory : public BrowserContextKeyedServiceFactory {
bool ServiceIsNULLWhileTesting() const override;
};
-} // namespace extensions
-
-#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLBAR_MODEL_FACTORY_H_
+#endif // CHROME_BROWSER_UI_TOOLBAR_TOOLBAR_ACTIONS_MODEL_FACTORY_H_
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
new file mode 100644
index 0000000..0f0338c
--- /dev/null
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
@@ -0,0 +1,1369 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/extensions/extension_action_manager.h"
+#include "chrome/browser/extensions/extension_action_test_util.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/test_extension_dir.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/extensions/unpacked_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
+#include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
+#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/mock_component_toolbar_actions_factory.h"
+#include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
+#include "chrome/common/extensions/api/extension_action/action_info.h"
+#include "components/crx_file/id_util.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/pref_names.h"
+#include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/feature_switch.h"
+#include "extensions/common/value_builder.h"
+
+namespace {
+
+// A simple observer that tracks the number of times certain events occur.
+class ToolbarActionsModelTestObserver : public ToolbarActionsModel::Observer {
+ public:
+ explicit ToolbarActionsModelTestObserver(ToolbarActionsModel* model);
+ ~ToolbarActionsModelTestObserver() override;
+
+ size_t inserted_count() const { return inserted_count_; }
+ size_t removed_count() const { return removed_count_; }
+ size_t moved_count() const { return moved_count_; }
+ int highlight_mode_count() const { return highlight_mode_count_; }
+ size_t initialized_count() const { return initialized_count_; }
+
+ private:
+ // ToolbarActionsModel::Observer:
+ void OnToolbarActionAdded(const std::string& id, int index) override {
+ ++inserted_count_;
+ }
+
+ void OnToolbarActionRemoved(const std::string& id) override {
+ ++removed_count_;
+ }
+
+ void OnToolbarActionMoved(const std::string& id, int index) override {
+ ++moved_count_;
+ }
+
+ void OnToolbarActionUpdated(const std::string& id) override {}
+
+ bool ShowToolbarActionPopup(const std::string& id,
+ bool grant_active_tab) override {
+ return false;
+ }
+
+ void OnToolbarVisibleCountChanged() override {}
+
+ void OnToolbarHighlightModeChanged(bool is_highlighting) override {
+ // Add one if highlighting, subtract one if not.
+ highlight_mode_count_ += is_highlighting ? 1 : -1;
+ }
+
+ void OnToolbarModelInitialized() override { ++initialized_count_; }
+
+ Browser* GetBrowser() override { return NULL; }
+
+ ToolbarActionsModel* model_;
+
+ size_t inserted_count_;
+ size_t removed_count_;
+ size_t moved_count_;
+ // Int because it could become negative (if something goes wrong).
+ int highlight_mode_count_;
+ size_t initialized_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToolbarActionsModelTestObserver);
+};
+
+ToolbarActionsModelTestObserver::ToolbarActionsModelTestObserver(
+ ToolbarActionsModel* model)
+ : model_(model),
+ inserted_count_(0),
+ removed_count_(0),
+ moved_count_(0),
+ highlight_mode_count_(0),
+ initialized_count_(0) {
+ model_->AddObserver(this);
+}
+
+ToolbarActionsModelTestObserver::~ToolbarActionsModelTestObserver() {
+ model_->RemoveObserver(this);
+}
+
+} // namespace
+
+class ToolbarActionsModelUnitTest
+ : public extensions::ExtensionServiceTestBase {
+ public:
+ ToolbarActionsModelUnitTest(){};
+ ~ToolbarActionsModelUnitTest() override{};
+
+ protected:
+ // Initialize the ExtensionService, ToolbarActionsModel, and
+ // ExtensionSystem.
+ void Init();
+
+ void TearDown() override;
+
+ // Adds or removes the given |extension| and verify success.
+ testing::AssertionResult AddExtension(
+ const scoped_refptr<const extensions::Extension>& extension)
+ WARN_UNUSED_RESULT;
+ testing::AssertionResult RemoveExtension(
+ const scoped_refptr<const extensions::Extension>& extension)
+ WARN_UNUSED_RESULT;
+
+ // Adds three extensions, all with browser actions.
+ testing::AssertionResult AddBrowserActionExtensions() WARN_UNUSED_RESULT;
+
+ // Adds three extensions, one each for browser action, page action, and no
+ // action, and are added in that order.
+ testing::AssertionResult AddActionExtensions() WARN_UNUSED_RESULT;
+
+ // Returns the action's id at the given index in the toolbar model, or empty
+ // if one does not exist.
+ // If |model| is specified, it is used. Otherwise, this defaults to
+ // |toolbar_model_|.
+ const std::string GetActionIdAtIndex(size_t index,
+ const ToolbarActionsModel* model) const;
+ const std::string GetActionIdAtIndex(size_t index) const;
+
+ void SetMockActionsFactory(MockComponentToolbarActionsFactory* factory);
+
+ ToolbarActionsModel* toolbar_model() { return toolbar_model_; }
+
+ const ToolbarActionsModelTestObserver* observer() const {
+ return model_observer_.get();
+ }
+ size_t num_toolbar_items() const {
+ return toolbar_model_->toolbar_items().size();
+ }
+ const extensions::Extension* browser_action_a() const {
+ return browser_action_a_.get();
+ }
+ const extensions::Extension* browser_action_b() const {
+ return browser_action_b_.get();
+ }
+ const extensions::Extension* browser_action_c() const {
+ return browser_action_c_.get();
+ }
+ const extensions::Extension* browser_action() const {
+ return browser_action_extension_.get();
+ }
+ const extensions::Extension* page_action() const {
+ return page_action_extension_.get();
+ }
+ const extensions::Extension* no_action() const {
+ return no_action_extension_.get();
+ }
+
+ // The mock component action will be referred to as "MCA" below.
+ const char* component_action_id() {
+ return ComponentToolbarActionsFactory::kActionIdForTesting;
+ }
+
+ private:
+ // Verifies that all extensions in |extensions| are added successfully.
+ testing::AssertionResult AddAndVerifyExtensions(
+ const extensions::ExtensionList& extensions);
+
+ // The toolbar model associated with the testing profile.
+ ToolbarActionsModel* toolbar_model_;
+
+ // The test observer to track events. Must come after toolbar_model_ so that
+ // it is destroyed and removes itself as an observer first.
+ scoped_ptr<ToolbarActionsModelTestObserver> model_observer_;
+
+ // Sample extensions with only browser actions.
+ scoped_refptr<const extensions::Extension> browser_action_a_;
+ scoped_refptr<const extensions::Extension> browser_action_b_;
+ scoped_refptr<const extensions::Extension> browser_action_c_;
+
+ // Sample extensions with different kinds of actions.
+ scoped_refptr<const extensions::Extension> browser_action_extension_;
+ scoped_refptr<const extensions::Extension> page_action_extension_;
+ scoped_refptr<const extensions::Extension> no_action_extension_;
+
+ scoped_ptr<MockComponentToolbarActionsFactory> mock_actions_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ToolbarActionsModelUnitTest);
+};
+
+void ToolbarActionsModelUnitTest::Init() {
+ InitializeEmptyExtensionService();
+ toolbar_model_ =
+ extensions::extension_action_test_util::CreateToolbarModelForProfile(
+ profile());
+ model_observer_.reset(new ToolbarActionsModelTestObserver(toolbar_model_));
+}
+
+void ToolbarActionsModelUnitTest::TearDown() {
+ model_observer_.reset();
+ extensions::ExtensionServiceTestBase::TearDown();
+}
+
+testing::AssertionResult ToolbarActionsModelUnitTest::AddExtension(
+ const scoped_refptr<const extensions::Extension>& extension) {
+ if (registry()->enabled_extensions().GetByID(extension->id())) {
+ return testing::AssertionFailure() << "Extension " << extension->name()
+ << " already installed!";
+ }
+ service()->AddExtension(extension.get());
+ if (!registry()->enabled_extensions().GetByID(extension->id())) {
+ return testing::AssertionFailure() << "Failed to install extension: "
+ << extension->name();
+ }
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult ToolbarActionsModelUnitTest::RemoveExtension(
+ const scoped_refptr<const extensions::Extension>& extension) {
+ if (!registry()->enabled_extensions().GetByID(extension->id())) {
+ return testing::AssertionFailure() << "Extension " << extension->name()
+ << " not installed!";
+ }
+ service()->UnloadExtension(extension->id(),
+ extensions::UnloadedExtensionInfo::REASON_DISABLE);
+ if (registry()->enabled_extensions().GetByID(extension->id())) {
+ return testing::AssertionFailure() << "Failed to unload extension: "
+ << extension->name();
+ }
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult ToolbarActionsModelUnitTest::AddActionExtensions() {
+ browser_action_extension_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "browser_action",
+ extensions::extension_action_test_util::BROWSER_ACTION);
+ page_action_extension_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "page_action", extensions::extension_action_test_util::PAGE_ACTION);
+ no_action_extension_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "no_action", extensions::extension_action_test_util::NO_ACTION);
+
+ extensions::ExtensionList extensions;
+ extensions.push_back(browser_action_extension_);
+ extensions.push_back(page_action_extension_);
+ extensions.push_back(no_action_extension_);
+
+ return AddAndVerifyExtensions(extensions);
+}
+
+testing::AssertionResult
+ToolbarActionsModelUnitTest::AddBrowserActionExtensions() {
+ browser_action_a_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "browser_actionA",
+ extensions::extension_action_test_util::BROWSER_ACTION);
+ browser_action_b_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "browser_actionB",
+ extensions::extension_action_test_util::BROWSER_ACTION);
+ browser_action_c_ =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "browser_actionC",
+ extensions::extension_action_test_util::BROWSER_ACTION);
+
+ extensions::ExtensionList extensions;
+ extensions.push_back(browser_action_a_);
+ extensions.push_back(browser_action_b_);
+ extensions.push_back(browser_action_c_);
+
+ return AddAndVerifyExtensions(extensions);
+}
+
+const std::string ToolbarActionsModelUnitTest::GetActionIdAtIndex(
+ size_t index,
+ const ToolbarActionsModel* model) const {
+ return index < model->toolbar_items().size()
+ ? model->toolbar_items()[index].id
+ : std::string();
+}
+
+const std::string ToolbarActionsModelUnitTest::GetActionIdAtIndex(
+ size_t index) const {
+ return GetActionIdAtIndex(index, toolbar_model_);
+}
+
+testing::AssertionResult ToolbarActionsModelUnitTest::AddAndVerifyExtensions(
+ const extensions::ExtensionList& extensions) {
+ for (extensions::ExtensionList::const_iterator iter = extensions.begin();
+ iter != extensions.end(); ++iter) {
+ if (!AddExtension(*iter)) {
+ return testing::AssertionFailure() << "Failed to install extension: "
+ << (*iter)->name();
+ }
+ }
+ return testing::AssertionSuccess();
+}
+
+void ToolbarActionsModelUnitTest::SetMockActionsFactory(
+ MockComponentToolbarActionsFactory* factory) {
+ mock_actions_factory_.reset(factory);
+}
+
+// A basic test for component actions and extensions with browser actions
+// showing up in the toolbar.
+TEST_F(ToolbarActionsModelUnitTest, BasicToolbarActionsModelTest) {
+ Init();
+
+ // Load an extension with no browser action.
+ scoped_refptr<const extensions::Extension> extension1 =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "no_action", extensions::extension_action_test_util::NO_ACTION);
+ ASSERT_TRUE(AddExtension(extension1));
+
+ // This extension should not be in the model (has no browser action).
+ EXPECT_EQ(0u, observer()->inserted_count());
+ EXPECT_EQ(0u, num_toolbar_items());
+ EXPECT_EQ(std::string(), GetActionIdAtIndex(0u));
+
+ // Load an extension with a browser action.
+ scoped_refptr<const extensions::Extension> extension2 =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "browser_action",
+ extensions::extension_action_test_util::BROWSER_ACTION);
+ ASSERT_TRUE(AddExtension(extension2));
+
+ // We should now find our extension in the model.
+ EXPECT_EQ(1u, observer()->inserted_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(extension2->id(), GetActionIdAtIndex(0u));
+
+ // Should be a no-op, but still fires the events.
+ toolbar_model()->MoveActionIcon(extension2->id(), 0);
+ EXPECT_EQ(1u, observer()->moved_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(extension2->id(), GetActionIdAtIndex(0u));
+
+ // Remove the extension and verify.
+ ASSERT_TRUE(RemoveExtension(extension2));
+ EXPECT_EQ(1u, observer()->removed_count());
+ EXPECT_EQ(0u, num_toolbar_items());
+ EXPECT_EQ(std::string(), GetActionIdAtIndex(0u));
+}
+
+// Test various different reorderings, removals, and reinsertions.
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarReorderAndReinsert) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ // Verify the three actions are in the model in the proper order.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+
+ // Order is now A, B, C. Let's put C first.
+ toolbar_model()->MoveActionIcon(browser_action_c()->id(), 0);
+ EXPECT_EQ(1u, observer()->moved_count());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+
+ // Order is now C, A, B. Let's put A last.
+ toolbar_model()->MoveActionIcon(browser_action_a()->id(), 2);
+ EXPECT_EQ(2u, observer()->moved_count());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(2u));
+
+ // Order is now C, B, A. Let's remove B.
+ ASSERT_TRUE(RemoveExtension(browser_action_b()));
+ EXPECT_EQ(1u, observer()->removed_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+
+ // Load extension B again.
+ ASSERT_TRUE(AddExtension(browser_action_b()));
+
+ // Extension B loaded again.
+ EXPECT_EQ(4u, observer()->inserted_count());
+ EXPECT_EQ(3u, num_toolbar_items());
+ // Make sure it gets its old spot in the list.
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+
+ // Unload B again.
+ ASSERT_TRUE(RemoveExtension(browser_action_b()));
+ EXPECT_EQ(2u, observer()->removed_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+
+ // Order is now C, A. Flip it.
+ toolbar_model()->MoveActionIcon(browser_action_a()->id(), 0);
+ EXPECT_EQ(3u, observer()->moved_count());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+
+ // Move A to the location it already occupies.
+ toolbar_model()->MoveActionIcon(browser_action_a()->id(), 0);
+ EXPECT_EQ(4u, observer()->moved_count());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+
+ // Order is now A, C.
+ ASSERT_TRUE(RemoveExtension(browser_action_c()));
+ EXPECT_EQ(3u, observer()->removed_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+
+ // Load extension C again.
+ ASSERT_TRUE(AddExtension(browser_action_c()));
+
+ // Extension C loaded again.
+ EXPECT_EQ(5u, observer()->inserted_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+ // Make sure it gets its old spot in the list (at the very end).
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+}
+
+// Test that order persists after unloading and disabling, but not across
+// uninstallation.
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarUnloadDisableAndUninstall) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ // Verify the three actions are in the model in the proper order: A, B, C.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+
+ // Unload B, then C, then A, and then reload C, then A, then B.
+ ASSERT_TRUE(RemoveExtension(browser_action_b()));
+ ASSERT_TRUE(RemoveExtension(browser_action_c()));
+ ASSERT_TRUE(RemoveExtension(browser_action_a()));
+ EXPECT_EQ(0u, num_toolbar_items()); // Sanity check: all gone?
+ ASSERT_TRUE(AddExtension(browser_action_c()));
+ ASSERT_TRUE(AddExtension(browser_action_a()));
+ ASSERT_TRUE(AddExtension(browser_action_b()));
+ EXPECT_EQ(3u, num_toolbar_items()); // Sanity check: all back?
+ EXPECT_EQ(0u, observer()->moved_count());
+
+ // Even though we unloaded and reloaded in a different order, the original
+ // order (A, B, C) should be preserved.
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+
+ // Disabling extensions should also preserve order.
+ service()->DisableExtension(browser_action_b()->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ service()->DisableExtension(browser_action_c()->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ service()->DisableExtension(browser_action_a()->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ service()->EnableExtension(browser_action_c()->id());
+ service()->EnableExtension(browser_action_a()->id());
+ service()->EnableExtension(browser_action_b()->id());
+
+ // Make sure we still get the original A, B, C order.
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+
+ // Move browser_action_b() to be first.
+ toolbar_model()->MoveActionIcon(browser_action_b()->id(), 0);
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u));
+
+ // Uninstall Extension B.
+ service()->UninstallExtension(browser_action_b()->id(),
+ extensions::UNINSTALL_REASON_FOR_TESTING,
+ base::Bind(&base::DoNothing),
+ NULL); // Ignore error.
+ // List contains only A and C now. Validate that.
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+
+ ASSERT_TRUE(AddExtension(browser_action_b()));
+
+ // Make sure Extension B is _not_ first (its old position should have been
+ // forgotten at uninstall time). Order should be A, C, B.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ReorderOnPrefChange) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ EXPECT_EQ(3u, num_toolbar_items());
+
+ // Change the value of the toolbar preference.
+ std::vector<std::string> new_order;
+ new_order.push_back(browser_action_c()->id());
+ new_order.push_back(browser_action_b()->id());
+ extensions::ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
+
+ // Verify order is changed.
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(2u));
+}
+
+// Test that new extension actions are always visible on installation and
+// inserted at the "end" of the visible section.
+TEST_F(ToolbarActionsModelUnitTest, NewToolbarExtensionsAreVisible) {
+ Init();
+
+ // Three extensions with actions.
+ scoped_refptr<const extensions::Extension> extension_a =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "a", extensions::extension_action_test_util::BROWSER_ACTION);
+ scoped_refptr<const extensions::Extension> extension_b =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "b", extensions::extension_action_test_util::BROWSER_ACTION);
+ scoped_refptr<const extensions::Extension> extension_c =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "c", extensions::extension_action_test_util::BROWSER_ACTION);
+ scoped_refptr<const extensions::Extension> extension_d =
+ extensions::extension_action_test_util::CreateActionExtension(
+ "d", extensions::extension_action_test_util::BROWSER_ACTION);
+
+ // We should start off without any actions.
+ EXPECT_EQ(0u, num_toolbar_items());
+ EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
+
+ // Add one action. It should be visible.
+ service()->AddExtension(extension_a.get());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(0u));
+
+ // Hide all actions.
+ toolbar_model()->SetVisibleIconCount(0);
+ EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
+
+ // Add a new action - it should be visible, so it should be in the first
+ // index. The other action should remain hidden.
+ service()->AddExtension(extension_b.get());
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
+
+ // Show all actions.
+ toolbar_model()->SetVisibleIconCount(2);
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+
+ // Add the third action. Since all action are visible, it should go in the
+ // last index.
+ service()->AddExtension(extension_c.get());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+ EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_c.get()->id(), GetActionIdAtIndex(2u));
+
+ // Hide one action (two remaining visible).
+ toolbar_model()->SetVisibleIconCount(2);
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+
+ // Add a fourth action. It should go at the end of the visible section and
+ // be visible, so it increases visible count by 1, and goes into the fourth
+ // index. The hidden action should remain hidden.
+ service()->AddExtension(extension_d.get());
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_d.get()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(extension_c.get()->id(), GetActionIdAtIndex(3u));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarHighlightMode) {
+ Init();
+
+ EXPECT_FALSE(toolbar_model()->HighlightActions(
+ std::vector<std::string>(), ToolbarActionsModel::HIGHLIGHT_WARNING));
+ EXPECT_EQ(0, observer()->highlight_mode_count());
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ EXPECT_EQ(3u, num_toolbar_items());
+
+ // Start with a visible count of 2 (non-zero, and not all).
+ toolbar_model()->SetVisibleIconCount(2u);
+
+ // Highlight one extension.
+ std::vector<std::string> action_ids;
+ action_ids.push_back(browser_action_b()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(1, observer()->highlight_mode_count());
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+
+ // Stop highlighting.
+ toolbar_model()->StopHighlighting();
+ EXPECT_EQ(0, observer()->highlight_mode_count());
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+
+ // Verify that the extensions are back to normal.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+
+ // Call stop highlighting a second time (shouldn't be notified).
+ toolbar_model()->StopHighlighting();
+ EXPECT_EQ(0, observer()->highlight_mode_count());
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+
+ // Highlight all extensions.
+ action_ids.clear();
+ action_ids.push_back(browser_action_a()->id());
+ action_ids.push_back(browser_action_b()->id());
+ action_ids.push_back(browser_action_c()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(1, observer()->highlight_mode_count());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+ // Even though the visible count is 3, we shouldn't adjust the stored
+ // preference.
+ EXPECT_EQ(2, profile()->GetPrefs()->GetInteger(
+ extensions::pref_names::kToolbarSize));
+
+ // Highlight only extension B (shrink the highlight list).
+ action_ids.clear();
+ action_ids.push_back(browser_action_b()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(2, observer()->highlight_mode_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u));
+
+ // Highlight extensions A and B (grow the highlight list).
+ action_ids.clear();
+ action_ids.push_back(browser_action_a()->id());
+ action_ids.push_back(browser_action_b()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(3, observer()->highlight_mode_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+
+ // Highlight no extensions (empty the highlight list).
+ action_ids.clear();
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(2, observer()->highlight_mode_count());
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+ // Our toolbar size should be back to normal.
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(2, profile()->GetPrefs()->GetInteger(
+ extensions::pref_names::kToolbarSize));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarHighlightModeRemove) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ EXPECT_EQ(3u, num_toolbar_items());
+
+ // Highlight two of the extensions.
+ std::vector<std::string> action_ids;
+ action_ids.push_back(browser_action_a()->id());
+ action_ids.push_back(browser_action_b()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1, observer()->highlight_mode_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+
+ // Disable one of them - only one should remain highlighted.
+ service()->DisableExtension(browser_action_a()->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u));
+
+ // Uninstall the remaining highlighted extension. This should result in
+ // highlight mode exiting.
+ service()->UninstallExtension(browser_action_b()->id(),
+ extensions::UNINSTALL_REASON_FOR_TESTING,
+ base::Bind(&base::DoNothing),
+ NULL); // Ignore error.
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(0, observer()->highlight_mode_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+
+ // Test that removing an unhighlighted extension still works.
+ // Reinstall extension B, and then highlight extension C.
+ ASSERT_TRUE(AddExtension(browser_action_b()));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ action_ids.clear();
+ action_ids.push_back(browser_action_c()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_EQ(1, observer()->highlight_mode_count());
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+
+ // Uninstalling B should not have visible impact.
+ service()->UninstallExtension(browser_action_b()->id(),
+ extensions::UNINSTALL_REASON_FOR_TESTING,
+ base::Bind(&base::DoNothing),
+ NULL); // Ignore error.
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1, observer()->highlight_mode_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+
+ // When we stop, only action C should remain.
+ toolbar_model()->StopHighlighting();
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(0, observer()->highlight_mode_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarHighlightModeAdd) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ EXPECT_EQ(3u, num_toolbar_items());
+
+ // Remove one (down to two).
+ ASSERT_TRUE(RemoveExtension(browser_action_c()));
+
+ // Highlight one of the two actions.
+ std::vector<std::string> action_ids;
+ action_ids.push_back(browser_action_a()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+
+ // Adding a new extension should have no visible effect.
+ ASSERT_TRUE(AddExtension(browser_action_c()));
+ EXPECT_TRUE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+
+ // When we stop highlighting, we should see the new extension show up.
+ toolbar_model()->StopHighlighting();
+ EXPECT_FALSE(toolbar_model()->is_highlighting());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+}
+
+// Test that the action toolbar maintains the proper size, even after a pref
+// change.
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarSizeAfterPrefChange) {
+ Init();
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ EXPECT_EQ(3u, num_toolbar_items());
+
+ // Should be at max size.
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+ EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
+ toolbar_model()->OnActionToolbarPrefChange();
+ // Should still be at max size.
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+ EXPECT_EQ(num_toolbar_items(), toolbar_model()->visible_icon_count());
+}
+
+// Test that, in the absence of the extension-action-redesign switch, the
+// model only contains extensions with browser actions and component actions.
+TEST_F(ToolbarActionsModelUnitTest, TestToolbarExtensionTypesNoSwitch) {
+ Init();
+ ASSERT_TRUE(AddActionExtensions());
+
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(browser_action()->id(), GetActionIdAtIndex(0u));
+}
+
+// Test that, with the extension-action-redesign switch, the model contains
+// all types of extensions, except those which should not be displayed on the
+// toolbar (like component extensions).
+TEST_F(ToolbarActionsModelUnitTest, TestToolbarExtensionTypesSwitch) {
+ extensions::FeatureSwitch::ScopedOverride enable_redesign(
+ extensions::FeatureSwitch::extension_action_redesign(), true);
+ Init();
+
+ ASSERT_TRUE(AddActionExtensions());
+
+ // With the switch on, extensions with page actions and no action should also
+ // be displayed in the toolbar.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(page_action()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(no_action()->id(), GetActionIdAtIndex(2u));
+}
+
+// Test that hiding actions on the toolbar results in their removal from the
+// model when the redesign switch is not enabled.
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarActionsVisibilityNoSwitch) {
+ Init();
+
+ extensions::ExtensionActionAPI* action_api =
+ extensions::ExtensionActionAPI::Get(profile());
+
+ ASSERT_TRUE(AddBrowserActionExtensions());
+ // Sanity check: Order should start as A , B, C.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+
+ // By default, all actions should be visible.
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_a()->id()));
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id()));
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_c()->id()));
+
+ // Hiding an action should result in its removal from the toolbar.
+ action_api->SetBrowserActionVisibility(browser_action_b()->id(), false);
+ EXPECT_FALSE(
+ action_api->GetBrowserActionVisibility(browser_action_b()->id()));
+ // Thus, there should now only be two items on the toolbar - A and C.
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+
+ // Resetting the visibility to 'true' should result in the extension being
+ // added back at its original position.
+ action_api->SetBrowserActionVisibility(browser_action_b()->id(), true);
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(browser_action_b()->id()));
+ // So the toolbar order should be A, B, C.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarIncognitoModeTest) {
+ Init();
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ // Give two extensions incognito access.
+ // Note: We use ExtensionPrefs::SetIsIncognitoEnabled instead of
+ // util::SetIsIncognitoEnabled because the latter tries to reload the
+ // extension, which requries a filepath associated with the extension (and,
+ // for this test, reloading the extension is irrelevant to us).
+ extensions::ExtensionPrefs* extension_prefs =
+ extensions::ExtensionPrefs::Get(profile());
+ extension_prefs->SetIsIncognitoEnabled(browser_action_b()->id(), true);
+ extension_prefs->SetIsIncognitoEnabled(browser_action_c()->id(), true);
+
+ extensions::util::SetIsIncognitoEnabled(browser_action_b()->id(), profile(),
+ true);
+ extensions::util::SetIsIncognitoEnabled(browser_action_c()->id(), profile(),
+ true);
+
+ // Move C to the second index.
+ toolbar_model()->MoveActionIcon(browser_action_c()->id(), 1u);
+ // Set visible count to 3 so that C is overflowed. State is A, C, [B].
+ toolbar_model()->SetVisibleIconCount(2);
+ EXPECT_EQ(1u, observer()->moved_count());
+
+ // Get an incognito profile and toolbar.
+ ToolbarActionsModel* incognito_model =
+ extensions::extension_action_test_util::CreateToolbarModelForProfile(
+ profile()->GetOffTheRecordProfile());
+
+ ToolbarActionsModelTestObserver incognito_observer(incognito_model);
+ EXPECT_EQ(0u, incognito_observer.moved_count());
+
+ // We should have two items: C, B, and the order should be preserved from the
+ // original model.
+ EXPECT_EQ(2u, incognito_model->toolbar_items().size());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u, incognito_model));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u, incognito_model));
+
+ // Actions in the overflow menu in the regular toolbar should remain in
+ // overflow in the incognito toolbar. So, we should have C, [B].
+ EXPECT_EQ(1u, incognito_model->visible_icon_count());
+ // The regular model should still have two icons visible.
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+
+ // Changing the incognito model size should not affect the regular model.
+ incognito_model->SetVisibleIconCount(0);
+ EXPECT_EQ(0u, incognito_model->visible_icon_count());
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+
+ // Expanding the incognito model to 3 should register as "all icons"
+ // since it is all of the incognito-enabled extensions.
+ incognito_model->SetVisibleIconCount(2u);
+ EXPECT_EQ(2u, incognito_model->visible_icon_count());
+ EXPECT_TRUE(incognito_model->all_icons_visible());
+
+ // Moving icons in the incognito toolbar should not affect the regular
+ // toolbar. Incognito currently has C, B...
+ incognito_model->MoveActionIcon(browser_action_b()->id(), 0u);
+ // So now it should be B, C...
+ EXPECT_EQ(1u, incognito_observer.moved_count());
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u, incognito_model));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u, incognito_model));
+ // ... and the regular toolbar should be unaffected.
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+
+ // Similarly, the observer for the regular model should not have received
+ // any updates.
+ EXPECT_EQ(1u, observer()->moved_count());
+
+ // And performing moves on the regular model should have no effect on the
+ // incognito model or its observers.
+ toolbar_model()->MoveActionIcon(browser_action_c()->id(), 2u);
+ EXPECT_EQ(2u, observer()->moved_count());
+ EXPECT_EQ(1u, incognito_observer.moved_count());
+}
+
+// Test that enabling extensions incognito with an active incognito profile
+// works.
+TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarIncognitoEnableExtension) {
+ Init();
+
+ const char* kManifest =
+ "{"
+ " \"name\": \"%s\","
+ " \"version\": \"1.0\","
+ " \"manifest_version\": 2,"
+ " \"browser_action\": {}"
+ "}";
+
+ // For this test, we need to have "real" extension files, because we need to
+ // be able to reload them during the incognito process. Since the toolbar
+ // needs to be notified of the reload, we need it this time (as opposed to
+ // above, where we simply set the prefs before the incognito bar was
+ // created.
+ extensions::TestExtensionDir dir1;
+ dir1.WriteManifest(base::StringPrintf(kManifest, "incognito1"));
+ extensions::TestExtensionDir dir2;
+ dir2.WriteManifest(base::StringPrintf(kManifest, "incognito2"));
+
+ extensions::TestExtensionDir* dirs[] = {&dir1, &dir2};
+ const extensions::Extension* extensions[] = {nullptr, nullptr};
+ for (size_t i = 0; i < arraysize(dirs); ++i) {
+ // The extension id will be calculated from the file path; we need this to
+ // wait for the extension to load.
+ base::FilePath path_for_id =
+ base::MakeAbsoluteFilePath(dirs[i]->unpacked_path());
+ std::string id = crx_file::id_util::GenerateIdForPath(path_for_id);
+ extensions::TestExtensionRegistryObserver observer(registry(), id);
+ extensions::UnpackedInstaller::Create(service())
+ ->Load(dirs[i]->unpacked_path());
+ observer.WaitForExtensionLoaded();
+ extensions[i] = registry()->enabled_extensions().GetByID(id);
+ ASSERT_TRUE(extensions[i]);
+ }
+
+ // For readability, alias to A and B. Since we'll be reloading these
+ // extensions, we also can't rely on pointers.
+ std::string extension_a = extensions[0]->id();
+ std::string extension_b = extensions[1]->id();
+
+ // The first model should have both extensions visible.
+ EXPECT_EQ(2u, toolbar_model()->toolbar_items().size());
+ EXPECT_EQ(extension_a, GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_b, GetActionIdAtIndex(1u));
+
+ // Set the model to only show one extension, so the order is A, [B].
+ toolbar_model()->SetVisibleIconCount(1u);
+
+ // Get an incognito profile and toolbar.
+ ToolbarActionsModel* incognito_model =
+ extensions::extension_action_test_util::CreateToolbarModelForProfile(
+ profile()->GetOffTheRecordProfile());
+ ToolbarActionsModelTestObserver incognito_observer(incognito_model);
+
+ // Right now, no actions are enabled in incognito mode.
+ EXPECT_EQ(0u, incognito_model->toolbar_items().size());
+
+ // Set extension B (which is overflowed) to be enabled in incognito. This
+ // results in b reloading, so wait for it.
+ {
+ extensions::TestExtensionRegistryObserver observer(registry(), extension_b);
+ extensions::util::SetIsIncognitoEnabled(extension_b, profile(), true);
+ observer.WaitForExtensionLoaded();
+ }
+
+ // Now, we should have one icon in the incognito bar. But, since B is
+ // overflowed in the main bar, it shouldn't be visible.
+ EXPECT_EQ(1u, incognito_model->toolbar_items().size());
+ EXPECT_EQ(extension_b, GetActionIdAtIndex(0u, incognito_model));
+ EXPECT_EQ(0u, incognito_model->visible_icon_count());
+
+ // Also enable extension a for incognito (again, wait for the reload).
+ {
+ extensions::TestExtensionRegistryObserver observer(registry(), extension_a);
+ extensions::util::SetIsIncognitoEnabled(extension_a, profile(), true);
+ observer.WaitForExtensionLoaded();
+ }
+
+ // Now, both extensions should be enabled in incognito mode. In addition, the
+ // incognito toolbar should have expanded to show extension A (since it isn't
+ // overflowed in the main bar).
+ EXPECT_EQ(2u, incognito_model->toolbar_items().size());
+ EXPECT_EQ(extension_a, GetActionIdAtIndex(0u, incognito_model));
+ EXPECT_EQ(extension_b, GetActionIdAtIndex(1u, incognito_model));
+ EXPECT_EQ(1u, incognito_model->visible_icon_count());
+}
+
+// Test that hiding actions on the toolbar results in sending them to the
+// overflow menu when the redesign switch is enabled.
+TEST_F(ToolbarActionsModelUnitTest,
+ ActionsToolbarActionsVisibilityWithSwitchAndComponentActions) {
+ extensions::FeatureSwitch::ScopedOverride enable_redesign(
+ extensions::FeatureSwitch::extension_action_redesign(), true);
+ Init();
+
+ // We choose to use all types of extensions here, since the misnamed
+ // BrowserActionVisibility is now for toolbar visibility.
+ ASSERT_TRUE(AddActionExtensions());
+
+ // For readability, alias extensions A B C.
+ const extensions::Extension* extension_a = browser_action();
+ const extensions::Extension* extension_b = page_action();
+ const extensions::Extension* extension_c = no_action();
+
+ // Sanity check: Order should start as A, B, C, with all three visible.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+ EXPECT_EQ(extension_a->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_b->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_c->id(), GetActionIdAtIndex(2u));
+
+ extensions::ExtensionActionAPI* action_api =
+ extensions::ExtensionActionAPI::Get(profile());
+
+ // By default, all actions should be visible.
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_a->id()));
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_c->id()));
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_b->id()));
+
+ // Hiding an action should result in it being sent to the overflow menu.
+ action_api->SetBrowserActionVisibility(extension_b->id(), false);
+
+ // Thus, the order should be A, C, B, with B in the overflow.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_a->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_c->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_b->id(), GetActionIdAtIndex(2u));
+
+ // Hiding an extension's action should result in it being sent to the overflow
+ // as well, but as the _first_ extension in the overflow.
+ action_api->SetBrowserActionVisibility(extension_a->id(), false);
+ // Thus, the order should be C, A, B, with A and B in the overflow.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_c->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_b->id(), GetActionIdAtIndex(2u));
+
+ // Resetting A's visibility to true should send it back to the visible icons
+ // (and should grow visible icons by 1), but it should be added to the end of
+ // the visible icon list (not to its original position).
+ action_api->SetBrowserActionVisibility(extension_a->id(), true);
+ // So order is C, A, B, with only B in the overflow.
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+ EXPECT_EQ(extension_c->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_b->id(), GetActionIdAtIndex(2u));
+
+ // Resetting B to be visible should make the order C, A, B, with no
+ // overflow.
+ action_api->SetBrowserActionVisibility(extension_b->id(), true);
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+ EXPECT_EQ(extension_c->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(extension_a->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(extension_b->id(), GetActionIdAtIndex(2u));
+
+ // Regression test for crbug.com/515963. Check that an extension's visibility
+ // is updated when it is moved out because another extension was removed.
+ toolbar_model()->SetVisibleIconCount(1);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(action_api->GetBrowserActionVisibility(extension_a->id()));
+ service()->DisableExtension(extension_c->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+ EXPECT_TRUE(action_api->GetBrowserActionVisibility(extension_a->id()));
+}
+
+// Test that observers receive no Added notifications until after the
+// ExtensionSystem has initialized.
+TEST_F(ToolbarActionsModelUnitTest, ModelWaitsForExtensionSystemReady) {
+ InitializeEmptyExtensionService();
+ ToolbarActionsModel* toolbar_model = extensions::extension_action_test_util::
+ CreateToolbarModelForProfileWithoutWaitingForReady(profile());
+ ToolbarActionsModelTestObserver model_observer(toolbar_model);
+
+ EXPECT_TRUE(AddBrowserActionExtensions());
+
+ // Since the model hasn't been initialized (the ExtensionSystem::ready task
+ // hasn't been run), there should be no insertion notifications.
+ EXPECT_EQ(0u, model_observer.inserted_count());
+ EXPECT_EQ(0u, model_observer.initialized_count());
+ EXPECT_FALSE(toolbar_model->actions_initialized());
+
+ // Run the ready task.
+ static_cast<extensions::TestExtensionSystem*>(
+ extensions::ExtensionSystem::Get(profile()))
+ ->SetReady();
+ // Run tasks posted to TestExtensionSystem.
+ base::RunLoop().RunUntilIdle();
+
+ // We should still have no insertions, but should have an initialized count.
+ EXPECT_TRUE(toolbar_model->actions_initialized());
+ EXPECT_EQ(0u, model_observer.inserted_count());
+ EXPECT_EQ(1u, model_observer.initialized_count());
+}
+
+// Check that the toolbar model correctly clears and reorders when it detects
+// a preference change.
+TEST_F(ToolbarActionsModelUnitTest, ToolbarModelPrefChange) {
+ Init();
+
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ // We should start in the basic A, B, C order.
+ ASSERT_TRUE(browser_action_a());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2));
+ // Record the difference between the inserted and removed counts. The actual
+ // value of the counts is not important, but we need to be sure that if we
+ // call to remove any, we also add them back.
+ size_t inserted_and_removed_difference =
+ observer()->inserted_count() - observer()->removed_count();
+
+ // Assign a new order, B, C, A, and write it in the prefs.
+ std::vector<std::string> new_order;
+ new_order.push_back(browser_action_b()->id());
+ new_order.push_back(browser_action_c()->id());
+ new_order.push_back(browser_action_a()->id());
+ extensions::ExtensionPrefs::Get(profile())->SetToolbarOrder(new_order);
+
+ // Ensure everything has time to run.
+ base::RunLoop().RunUntilIdle();
+
+ // The new order should be reflected in the model.
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(2));
+ EXPECT_EQ(inserted_and_removed_difference,
+ observer()->inserted_count() - observer()->removed_count());
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ComponentExtensionsAddedToEnd) {
+ Init();
+
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2));
+
+ const char kName[] = "component";
+ extensions::DictionaryBuilder manifest;
+ manifest.Set("name", kName)
+ .Set("description", "An extension")
+ .Set("manifest_version", 2)
+ .Set("version", "1.0.0")
+ .Set("browser_action", extensions::DictionaryBuilder().Pass());
+ scoped_refptr<const extensions::Extension> component_extension =
+ extensions::ExtensionBuilder()
+ .SetManifest(manifest.Pass())
+ .SetID(crx_file::id_util::GenerateId(kName))
+ .SetLocation(extensions::Manifest::COMPONENT)
+ .Build();
+ service()->AddExtension(component_extension.get());
+
+ EXPECT_EQ(component_extension.get()->id(), GetActionIdAtIndex(0));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(3));
+}
+
+TEST_F(ToolbarActionsModelUnitTest, ToolbarModelHighlightsForToolbarRedesign) {
+ extensions::FeatureSwitch::ScopedOverride enable_redesign(
+ extensions::FeatureSwitch::extension_action_redesign(), true);
+ InitializeEmptyExtensionService();
+ EXPECT_TRUE(AddActionExtensions());
+ ToolbarActionsModel* toolbar_model =
+ extensions::extension_action_test_util::CreateToolbarModelForProfile(
+ profile());
+ EXPECT_TRUE(toolbar_model);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
+ profile()));
+ EXPECT_TRUE(toolbar_model->is_highlighting());
+ EXPECT_EQ(ToolbarActionsModel::HIGHLIGHT_INFO,
+ toolbar_model->highlight_type());
+ EXPECT_EQ(3u, toolbar_model->visible_icon_count());
+ EXPECT_EQ(3u, toolbar_model->toolbar_items().size());
+
+ scoped_ptr<ToolbarActionsBarBubbleDelegate> bubble(
+ new ExtensionToolbarIconSurfacingBubbleDelegate(profile()));
+ bubble->OnBubbleClosed(ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS);
+
+ EXPECT_FALSE(toolbar_model->is_highlighting());
+ EXPECT_EQ(ToolbarActionsModel::HIGHLIGHT_NONE,
+ toolbar_model->highlight_type());
+}
+
+// Test various different reorderings, removals, and reinsertions of the
+// toolbar with component actions.
+TEST_F(ToolbarActionsModelUnitTest,
+ ActionsToolbarReorderAndReinsertWithSwitchAndCOmponentActions) {
+ extensions::FeatureSwitch::ScopedOverride enable_redesign(
+ extensions::FeatureSwitch::extension_action_redesign(), true);
+ SetMockActionsFactory(new MockComponentToolbarActionsFactory(nullptr));
+ Init();
+
+ // One component action was added when the model was initialized.
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(0u));
+
+ // Add the three browser action extensions.
+ ASSERT_TRUE(AddBrowserActionExtensions());
+
+ // Verify the four actions are in the model in the proper order.
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(3u));
+
+ // Order is now MCA, A, B, C. Let's put C first.
+ toolbar_model()->MoveActionIcon(browser_action_c()->id(), 0);
+ EXPECT_EQ(1u, observer()->moved_count());
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(3u));
+
+ // Order is now C, MCA, A, B. Let's put MCA last.
+ toolbar_model()->MoveActionIcon(component_action_id(), 3);
+ EXPECT_EQ(2u, observer()->moved_count());
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(3u));
+
+ // Order is now C, A, B, MCA. Move MCA to the location it already occupies.
+ toolbar_model()->MoveActionIcon(component_action_id(), 3);
+ EXPECT_EQ(3u, observer()->moved_count());
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(3u));
+
+ // Order is still C, A, B, MCA. Move MCA to second to last, in preparation
+ // for visibility checks.
+ toolbar_model()->MoveActionIcon(component_action_id(), 2);
+ EXPECT_EQ(4u, observer()->moved_count());
+ EXPECT_EQ(4u, num_toolbar_items());
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(2u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(3u));
+
+ // Order is now C, A, MCA, B. Show only three icons: C, A, MCA, [B].
+ toolbar_model()->SetVisibleIconCount(3);
+ EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+ EXPECT_FALSE(toolbar_model()->all_icons_visible());
+
+ // Show only two icons so we test MCA in the overflow. The icons should
+ // be: C, A, [MCA], [B].
+ toolbar_model()->SetVisibleIconCount(2);
+ EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+ EXPECT_FALSE(toolbar_model()->all_icons_visible());
+
+ // Show all the icons again. Order should be C, A, MCA, B.
+ toolbar_model()->SetVisibleIconCount(4);
+ EXPECT_EQ(4u, toolbar_model()->visible_icon_count());
+ EXPECT_TRUE(toolbar_model()->all_icons_visible());
+
+ // Order is C, A, MCA, B. Remove C.
+ ASSERT_TRUE(RemoveExtension(browser_action_c()));
+ EXPECT_EQ(1u, observer()->removed_count());
+ EXPECT_EQ(3u, num_toolbar_items());
+ EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(1u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+
+ // Order is now A, MCA, B. Remove A.
+ ASSERT_TRUE(RemoveExtension(browser_action_a()));
+ EXPECT_EQ(2u, observer()->removed_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
+
+ // Order is now MCA, B. Remove B.
+ ASSERT_TRUE(RemoveExtension(browser_action_b()));
+ EXPECT_EQ(3u, observer()->removed_count());
+ EXPECT_EQ(1u, num_toolbar_items());
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(0u));
+
+ // Load extension C again.
+ ASSERT_TRUE(AddExtension(browser_action_c()));
+ EXPECT_EQ(4u, observer()->inserted_count());
+ EXPECT_EQ(2u, num_toolbar_items());
+ // Make sure it gets its old spot in the list (at the beginning).
+ EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u));
+ EXPECT_EQ(component_action_id(), GetActionIdAtIndex(1u));
+}
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc
index a55fb98..5f9ca12 100644
--- a/chrome/browser/ui/toolbar/wrench_menu_model.cc
+++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc
@@ -16,7 +16,6 @@
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/defaults.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
@@ -35,9 +34,9 @@
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h"
-#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/upgrade_detector.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
@@ -89,7 +88,7 @@ namespace {
#if defined(OS_MACOSX)
// An empty command used because of a bug in AppKit menus.
-// See comment in CreateExtensionToolbarOverflowMenu().
+// See comment in CreateActionToolbarOverflowMenu().
const int kEmptyMenuItemCommand = 0;
#endif
@@ -786,7 +785,7 @@ bool WrenchMenuModel::IsCommandIdVisible(int command_id) const {
switch (command_id) {
#if defined(OS_MACOSX)
case kEmptyMenuItemCommand:
- return false; // Always hidden (see CreateExtensionToolbarOverflowMenu).
+ return false; // Always hidden (see CreateActionToolbarOverflowMenu).
#endif
#if defined(OS_WIN)
case IDC_VIEW_INCOMPATIBILITIES: {
@@ -883,7 +882,7 @@ bool WrenchMenuModel::ShouldShowNewIncognitoWindowMenuItem() {
// - Browser relaunch, quit.
void WrenchMenuModel::Build() {
if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled())
- CreateExtensionToolbarOverflowMenu();
+ CreateActionToolbarOverflowMenu();
AddItem(IDC_VIEW_INCOMPATIBILITIES,
l10n_util::GetStringUTF16(IDS_VIEW_INCOMPATIBILITIES));
@@ -1030,15 +1029,10 @@ bool WrenchMenuModel::AddGlobalErrorMenuItems() {
return menu_items_added;
}
-void WrenchMenuModel::CreateExtensionToolbarOverflowMenu() {
+void WrenchMenuModel::CreateActionToolbarOverflowMenu() {
// We only add the extensions overflow container if there are any icons that
- // aren't shown in the main container or if there are component actions.
- // TODO(apacible): Remove check for component actions when
- // ExtensionToolbarModel can support them.
- if (!extensions::ExtensionToolbarModel::Get(browser_->profile())->
- all_icons_visible() ||
- ComponentToolbarActionsFactory::GetInstance()->
- GetNumComponentActions(browser_) > 0) {
+ // aren't shown in the main container.
+ if (!ToolbarActionsModel::Get(browser_->profile())->all_icons_visible()) {
#if defined(OS_MACOSX)
// There's a bug in AppKit menus, where if a menu item with a custom view
// (like the extensions overflow menu) is the first menu item, it is not
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.h b/chrome/browser/ui/toolbar/wrench_menu_model.h
index b6751ef..525f47d 100644
--- a/chrome/browser/ui/toolbar/wrench_menu_model.h
+++ b/chrome/browser/ui/toolbar/wrench_menu_model.h
@@ -193,8 +193,8 @@ class WrenchMenuModel : public ui::SimpleMenuModel,
// clipboard menu content and the finalizing menu break.
void CreateCutCopyPasteMenu();
- // Add a menu item for the extension icons.
- void CreateExtensionToolbarOverflowMenu();
+ // Add a menu item for the browser action icons.
+ void CreateActionToolbarOverflowMenu();
// Appends everything needed for the zoom menu: a menu break, then the zoom
// menu content and then another menu break.
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index b94289a..07a9625 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -7,7 +7,6 @@
#include "base/compiler_specific.h"
#include "base/stl_util.h"
#include "chrome/browser/extensions/extension_message_bubble_controller.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
@@ -15,6 +14,7 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/toolbar/wrench_menu_badge_controller.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
@@ -702,10 +702,10 @@ void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
// paint (one will be triggered by entering highlight mode).
if (toolbar_actions_bar_->is_highlighting() &&
!toolbar_action_views_.empty() && !in_overflow_mode()) {
- extensions::ExtensionToolbarModel::HighlightType highlight_type =
+ ToolbarActionsModel::HighlightType highlight_type =
toolbar_actions_bar_->highlight_type();
views::Painter* painter =
- highlight_type == extensions::ExtensionToolbarModel::HIGHLIGHT_INFO
+ highlight_type == ToolbarActionsModel::HIGHLIGHT_INFO
? info_highlight_painter_.get()
: warning_highlight_painter_.get();
views::Painter::PaintPainterAt(canvas, painter, GetLocalBounds());
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
index 05d3dbc..9988a54 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container_browsertest.cc
@@ -6,14 +6,15 @@
#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include "chrome/browser/extensions/browser_action_test_util.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/toolbar/browser_actions_bar_browsertest.h"
+#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "ui/base/dragdrop/drop_target_event.h"
#include "ui/base/dragdrop/os_exchange_data.h"
@@ -61,10 +62,15 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest, DragBrowserActions) {
EXPECT_EQ(extension_a()->id(), browser_actions_bar()->GetExtensionId(1));
EXPECT_EQ(extension_c()->id(), browser_actions_bar()->GetExtensionId(2));
+ const extensions::ExtensionSet& extension_set =
+ extensions::ExtensionRegistry::Get(profile())->enabled_extensions();
+ const std::vector<ToolbarActionsModel::ToolbarItem>& toolbar_items =
+ toolbar_model()->toolbar_items();
+
// This order should be reflected in the underlying model.
- EXPECT_EQ(extension_b(), toolbar_model()->toolbar_items()[0].get());
- EXPECT_EQ(extension_a(), toolbar_model()->toolbar_items()[1].get());
- EXPECT_EQ(extension_c(), toolbar_model()->toolbar_items()[2].get());
+ EXPECT_EQ(extension_b(), extension_set.GetByID(toolbar_items[0].id));
+ EXPECT_EQ(extension_a(), extension_set.GetByID(toolbar_items[1].id));
+ EXPECT_EQ(extension_c(), extension_set.GetByID(toolbar_items[2].id));
// Simulate a drag and drop to the left.
ui::OSExchangeData drop_data2;
@@ -181,7 +187,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest, MultipleWindows) {
// Test that the BrowserActionsContainer responds correctly when the underlying
// model enters highlight mode, and that browser actions are undraggable in
// highlight mode. (Highlight mode itself it tested more thoroughly in the
-// ExtensionToolbarModel browsertests).
+// ToolbarActionsModel browsertests).
IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest, HighlightMode) {
LoadExtensions();
@@ -198,11 +204,11 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsBarBrowserTest, HighlightMode) {
gfx::Point point(action_view->x(), action_view->y());
EXPECT_TRUE(container->CanStartDragForView(action_view, point, point));
- extensions::ExtensionIdList extension_ids;
- extension_ids.push_back(extension_a()->id());
- extension_ids.push_back(extension_b()->id());
- toolbar_model()->HighlightExtensions(
- extension_ids, extensions::ExtensionToolbarModel::HIGHLIGHT_WARNING);
+ std::vector<std::string> action_ids;
+ action_ids.push_back(extension_a()->id());
+ action_ids.push_back(extension_b()->id());
+ toolbar_model()->HighlightActions(action_ids,
+ ToolbarActionsModel::HIGHLIGHT_WARNING);
// Only two browser actions should be visible.
EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
@@ -342,7 +348,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionsContainerOverflowTest,
// Move extension C to the first position. Order should now be C A B, with
// C and A visible in the main bar.
- toolbar_model()->MoveExtensionIcon(extension_c()->id(), 0);
+ toolbar_model()->MoveActionIcon(extension_c()->id(), 0);
overflow_bar()->Layout(); // Kick.
EXPECT_EQ(extension_c()->id(), main_bar()->GetIdAt(0u));
EXPECT_EQ(extension_a()->id(), main_bar()->GetIdAt(1u));
diff --git a/chrome/browser/ui/views/toolbar/chevron_menu_button.cc b/chrome/browser/ui/views/toolbar/chevron_menu_button.cc
index 57ca038..25b5f70 100644
--- a/chrome/browser/ui/views/toolbar/chevron_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/chevron_menu_button.cc
@@ -12,7 +12,6 @@
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_icon_factory.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
-#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/extensions/extension_action_view_controller.h"
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 3558125..7996c5e 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -709,10 +709,6 @@
'browser/extensions/extension_system_impl.h',
'browser/extensions/extension_tab_util.cc',
'browser/extensions/extension_tab_util.h',
- 'browser/extensions/extension_toolbar_model.cc',
- 'browser/extensions/extension_toolbar_model.h',
- 'browser/extensions/extension_toolbar_model_factory.cc',
- 'browser/extensions/extension_toolbar_model_factory.h',
'browser/extensions/extension_ui_util.cc',
'browser/extensions/extension_ui_util.h',
'browser/extensions/extension_uninstall_dialog.cc',
@@ -870,6 +866,10 @@
'browser/extensions/window_controller_list_observer.h',
'browser/extensions/zipfile_installer.cc',
'browser/extensions/zipfile_installer.h',
+ 'browser/ui/toolbar/toolbar_actions_model.cc',
+ 'browser/ui/toolbar/toolbar_actions_model.h',
+ 'browser/ui/toolbar/toolbar_actions_model_factory.cc',
+ 'browser/ui/toolbar/toolbar_actions_model_factory.h',
'browser/web_applications/update_shortcut_worker_win.cc',
'browser/web_applications/update_shortcut_worker_win.h',
'browser/web_applications/web_app.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index ade51cc..51d8b7e 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -522,6 +522,8 @@
'browser/ui/toolbar/browser_actions_bar_browsertest.cc',
'browser/ui/toolbar/browser_actions_bar_browsertest.h',
'browser/ui/toolbar/component_toolbar_actions_browsertest.cc',
+ 'browser/ui/toolbar/mock_component_toolbar_actions_factory.cc',
+ 'browser/ui/toolbar/mock_component_toolbar_actions_factory.h',
'browser/ui/toolbar/test_toolbar_model.cc',
'browser/ui/toolbar/test_toolbar_model.h',
'browser/ui/website_settings/mock_permission_bubble_view.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index b93eee2..09d4b36 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -736,7 +736,6 @@
'browser/extensions/extension_special_storage_policy_unittest.cc',
'browser/extensions/extension_sync_data_unittest.cc',
'browser/extensions/extension_test_message_listener_unittest.cc',
- 'browser/extensions/extension_toolbar_model_unittest.cc',
'browser/extensions/extension_user_script_loader_unittest.cc',
'browser/extensions/extension_web_ui_unittest.cc',
'browser/extensions/external_policy_loader_unittest.cc',
@@ -1561,6 +1560,8 @@
'browser/ui/toolbar/back_forward_menu_model_unittest.cc',
'browser/ui/toolbar/encoding_menu_controller_unittest.cc',
'browser/ui/toolbar/media_router_action_unittest.cc',
+ 'browser/ui/toolbar/mock_component_toolbar_actions_factory.cc',
+ 'browser/ui/toolbar/mock_component_toolbar_actions_factory.h',
'browser/ui/toolbar/recent_tabs_builder_test_helper.cc',
'browser/ui/toolbar/recent_tabs_builder_test_helper.h',
'browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc',
@@ -1568,6 +1569,7 @@
'browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h',
'browser/ui/toolbar/test_toolbar_model.cc',
'browser/ui/toolbar/test_toolbar_model.h',
+ 'browser/ui/toolbar/toolbar_actions_model_unittest.cc',
'browser/ui/toolbar/toolbar_actions_bar_unittest.cc',
'browser/ui/toolbar/toolbar_actions_bar_unittest.h',
'browser/ui/toolbar/toolbar_model_unittest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index a18553e..5b0c603 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -446,10 +446,8 @@ const char kEnableMaterialDesignDownloads[] = "enable-md-downloads";
// Enables the material design Settings feature.
const char kEnableMaterialDesignSettings[] = "enable-md-settings";
-#if defined(ENABLE_MEDIA_ROUTER)
// Enables Media Router.
const char kEnableMediaRouter[] = "enable-media-router";
-#endif
// Runs the Native Client inside the renderer process and enables GPU plugin
// (internally adds lEnableGpuPlugin to the command line).
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 77cb470..9b3bc08 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -46793,6 +46793,38 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
<summary>Number of retries until the final response was recorded.</summary>
</histogram>
+<histogram name="Toolbar.ActionsModel.ComponentActionsCount">
+ <owner>rdevlin.cronin@chromium.org</owner>
+ <summary>
+ The number of component action icons the Browser Actions Container knows
+ about (visible or in the overflow bucket). Does not count icons that have
+ been permanently hidden by the user. Measured once per startup per
+ (non-incognito) profile.
+ </summary>
+</histogram>
+
+<histogram name="Toolbar.ActionsModel.OverallActionsCount">
+ <owner>rdevlin.cronin@chromium.org</owner>
+ <summary>
+ The total number of action icons the Browser Actions Container knows about
+ (visible or in the overflow bucket). Does not count icons that have been
+ permanently hidden by the user. Measured once per startup per
+ (non-incognito) profile.
+ </summary>
+</histogram>
+
+<histogram name="Toolbar.ActionsModel.ToolbarActionsVisible">
+ <owner>rdevlin.cronin@chromium.org</owner>
+ <summary>
+ The number of visible toolbar icons in the Browser Actions Container
+ (visible as in number of icons not in the overflow bucket). 0 means all
+ icons are in the overflow bucket. MAX_INT means the toolbar is always
+ showing all icons. Measured once per startup per (non-incognito) profile but
+ only for those profiles that have one or more browser actions showing in the
+ toolbar.
+ </summary>
+</histogram>
+
<histogram name="TopSites.NumberOfApplyBlacklist">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner>
<summary>The number of times TopSitesImpl::ApplyBlacklist is called.</summary>