diff options
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> |