| Index: chrome/browser/ui/app_list/app_list_extension_sorting.cc
|
| diff --git a/chrome/browser/ui/app_list/app_list_extension_sorting.cc b/chrome/browser/ui/app_list/app_list_extension_sorting.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..46c8eecd9392d342369aa0976258f18c89efe157
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/app_list/app_list_extension_sorting.cc
|
| @@ -0,0 +1,329 @@
|
| +// Copyright (c) 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/chrome_notification_types.h"
|
| +#include "chrome/browser/extensions/extension_prefs.h"
|
| +#include "chrome/browser/extensions/extension_service.h"
|
| +#include "chrome/browser/ui/app_list/app_list_extension_sorting.h"
|
| +#include "chrome/browser/ui/app_list/app_list_extension_sorting_observer.h"
|
| +#include "content/public/browser/notification_service.h"
|
| +
|
| +#if defined(OS_CHROMEOS)
|
| +#include "chrome/browser/chromeos/extensions/default_app_order.h"
|
| +#endif
|
| +
|
| +namespace {
|
| +
|
| +const char kPrefAppListOrdinal[] = "app_list_ordinal";
|
| +
|
| +} // namespace
|
| +
|
| +AppListExtensionSorting::AppListExtensionSorting(
|
| + ExtensionScopedPrefs* extension_scoped_prefs)
|
| + : extension_scoped_prefs_(extension_scoped_prefs),
|
| + extension_service_(NULL) {}
|
| +
|
| +AppListExtensionSorting::~AppListExtensionSorting() {}
|
| +
|
| +void AppListExtensionSorting::Initialize(
|
| + const extensions::ExtensionIdList& extension_ids,
|
| + const extensions::ExtensionIdList& ntp_sorted_extension_ids) {
|
| + ExtensionIdSet uninitialized_extensions;
|
| + for (extensions::ExtensionIdList::const_iterator ext_it =
|
| + extension_ids.begin(); ext_it != extension_ids.end(); ++ext_it) {
|
| + if (!InitializeOrdinalFromPrefs(*ext_it))
|
| + uninitialized_extensions.insert(*ext_it);
|
| + // Ensure that the web store app still isn't found in this list, since
|
| + // it is added after this loop.
|
| + DCHECK(*ext_it != extension_misc::kWebStoreAppId);
|
| + DCHECK(*ext_it != extension_misc::kChromeAppId);
|
| + }
|
| +
|
| + // Include the Web Store App since it is displayed on the app list.
|
| + if (!InitializeOrdinalFromPrefs(extension_misc::kWebStoreAppId))
|
| + uninitialized_extensions.insert(extension_misc::kWebStoreAppId);
|
| + // Include the Chrome App since it is displayed on the app list.
|
| + if (!InitializeOrdinalFromPrefs(extension_misc::kChromeAppId))
|
| + uninitialized_extensions.insert(extension_misc::kChromeAppId);
|
| +
|
| + // Copy NTP app order into new pref.
|
| + InitializeFromNTPExtensionSorting(ntp_sorted_extension_ids,
|
| + &uninitialized_extensions);
|
| +
|
| + InitializeWithDefaultOrdinals(uninitialized_extensions);
|
| +}
|
| +
|
| +bool AppListExtensionSorting::InitializeOrdinalFromPrefs(
|
| + const std::string& extension_id) {
|
| + syncer::StringOrdinal ordinal = GetAppListOrdinalFromPrefs(extension_id);
|
| +
|
| + if (ordinal.IsValid())
|
| + SetAppListOrdinal(extension_id, ordinal);
|
| + return ordinal.IsValid();
|
| +}
|
| +
|
| +void AppListExtensionSorting::InitializeFromNTPExtensionSorting(
|
| + const extensions::ExtensionIdList& ntp_sorted_extension_ids,
|
| + ExtensionIdSet* uninitialized_extensions) {
|
| + for (extensions::ExtensionIdList::const_iterator ntp_it =
|
| + ntp_sorted_extension_ids.begin();
|
| + ntp_it != ntp_sorted_extension_ids.end(); ++ntp_it) {
|
| + if (uninitialized_extensions->count(*ntp_it)) {
|
| + syncer::StringOrdinal ordinal = app_list_ordinal_map_.empty()
|
| + ? syncer::StringOrdinal::CreateInitialOrdinal()
|
| + : app_list_ordinal_map_.rbegin()->first.CreateAfter();
|
| + SetAppListOrdinal(*ntp_it, ordinal);
|
| + UpdatePrefs(*ntp_it, ordinal);
|
| + uninitialized_extensions->erase(*ntp_it);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void AppListExtensionSorting::InitializeWithDefaultOrdinals(
|
| + const ExtensionIdSet& uninitialized_extensions) {
|
| + // The following defines the default order of apps.
|
| +#if defined(OS_CHROMEOS)
|
| + std::vector<std::string> default_app_order;
|
| + chromeos::default_app_order::Get(&default_app_order);
|
| +#else
|
| + const char* kDefaultAppOrder[] = {
|
| + extension_misc::kChromeAppId,
|
| + extension_misc::kWebStoreAppId,
|
| + };
|
| + const std::vector<const char*> default_app_order(
|
| + kDefaultAppOrder, kDefaultAppOrder + arraysize(kDefaultAppOrder));
|
| +#endif
|
| +
|
| + syncer::StringOrdinal ordinal = app_list_ordinal_map_.empty()
|
| + ? syncer::StringOrdinal::CreateInitialOrdinal()
|
| + : app_list_ordinal_map_.begin()->first.CreateBefore();
|
| +
|
| + // Create a map of extension ids to their default ordinals. The first ordinal
|
| + // used will precede all ordinals in the app list. Subsequent ordinals are
|
| + // created between this starting ordinal and the first ordinal in the app
|
| + // list.
|
| + std::map<std::string, syncer::StringOrdinal> default_ordinals;
|
| + for (size_t i = 0; i < default_app_order.size(); ++i) {
|
| + const std::string extension_id = default_app_order[i];
|
| + default_ordinals[extension_id] = ordinal;
|
| + ordinal = app_list_ordinal_map_.empty()
|
| + ? ordinal.CreateAfter()
|
| + : ordinal.CreateBetween(app_list_ordinal_map_.begin()->first);
|
| + }
|
| +
|
| + // Set default ordinals only for uninitialized extensions.
|
| + for (ExtensionIdSet::const_iterator it = uninitialized_extensions.begin();
|
| + it != uninitialized_extensions.end(); ++it) {
|
| + std::map<std::string, syncer::StringOrdinal>::const_iterator default_it =
|
| + default_ordinals.find(*it);
|
| + if (default_it != default_ordinals.end()) {
|
| + SetAppListOrdinal(*it, default_it->second);
|
| + UpdatePrefs(*it, default_it->second);
|
| + default_initialized_extensions_.push_back(*it);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void AppListExtensionSorting::FixSyncCollisions() {
|
| + for (AppListOrdinalMap::iterator ordinal_map_it =
|
| + app_list_ordinal_map_.begin();
|
| + ordinal_map_it != app_list_ordinal_map_.end(); ++ordinal_map_it) {
|
| + syncer::StringOrdinal repeated_ordinal = ordinal_map_it->first;
|
| + ExtensionIdSet& extension_set = ordinal_map_it->second;
|
| + if (extension_set.size() == 1)
|
| + continue;
|
| + // Assign unique ordinals to conflicting extensions by assigning new
|
| + // ordinals between the conflict ordinal and the next ordinal.
|
| + AppListOrdinalMap::iterator next_it = ordinal_map_it;
|
| + std::advance(next_it, 1);
|
| +
|
| + syncer::StringOrdinal upper_bound_ordinal =
|
| + next_it == app_list_ordinal_map_.end() ? syncer::StringOrdinal()
|
| + : next_it->first;
|
| + syncer::StringOrdinal lower_bound_ordinal = repeated_ordinal;
|
| + std::vector<std::string> conflicting_ids(extension_set.begin(),
|
| + extension_set.end());
|
| + // Skip the first conflicting extension because the first extension can keep
|
| + // the conflicted ordinal. Unique ordinals will be assigned so that
|
| + // conflicts are sorted by their extension ids.
|
| + for (size_t i = 1; i < conflicting_ids.size(); ++i) {
|
| + syncer::StringOrdinal unique_ordinal;
|
| + if (upper_bound_ordinal.IsValid()) {
|
| + unique_ordinal =
|
| + lower_bound_ordinal.CreateBetween(upper_bound_ordinal);
|
| + } else {
|
| + unique_ordinal = lower_bound_ordinal.CreateAfter();
|
| + }
|
| + EraseAppListOrdinal(conflicting_ids[i]);
|
| + SetAppListOrdinal(conflicting_ids[i], unique_ordinal);
|
| + UpdatePrefs(conflicting_ids[i], unique_ordinal);
|
| + lower_bound_ordinal = unique_ordinal;
|
| + }
|
| + }
|
| +
|
| + FOR_EACH_OBSERVER(AppListExtensionSortingObserver, observers_,
|
| + OnAppListReordered());
|
| +}
|
| +
|
| +void AppListExtensionSorting::InsertAtFront(const std::string& extension_id) {
|
| + DCHECK(!Contains(extension_id));
|
| + syncer::StringOrdinal ordinal = app_list_ordinal_map_.empty()
|
| + ? syncer::StringOrdinal::CreateInitialOrdinal()
|
| + : app_list_ordinal_map_.begin()->first.CreateBefore();
|
| + SetAppListOrdinal(extension_id, ordinal);
|
| + UpdatePrefs(extension_id, ordinal);
|
| +}
|
| +
|
| +void AppListExtensionSorting::InsertAtBack(const std::string& extension_id) {
|
| + DCHECK(!Contains(extension_id));
|
| + syncer::StringOrdinal ordinal = app_list_ordinal_map_.empty()
|
| + ? syncer::StringOrdinal::CreateInitialOrdinal()
|
| + : app_list_ordinal_map_.rbegin()->first.CreateAfter();
|
| + SetAppListOrdinal(extension_id, ordinal);
|
| + UpdatePrefs(extension_id, ordinal);
|
| +}
|
| +
|
| +void AppListExtensionSorting::OnExtensionMoved(
|
| + const std::string& moved_extension_id,
|
| + const std::string& predecessor_extension_id,
|
| + const std::string& successor_extension_id) {
|
| + const syncer::StringOrdinal& predecessor_ordinal =
|
| + predecessor_extension_id.empty() ?
|
| + syncer::StringOrdinal() :
|
| + GetAppListOrdinalFromPrefs(predecessor_extension_id);
|
| + const syncer::StringOrdinal& successor_ordinal =
|
| + successor_extension_id.empty() ?
|
| + syncer::StringOrdinal() :
|
| + GetAppListOrdinalFromPrefs(successor_extension_id);
|
| + // We only need to change the StringOrdinal if there is at least one
|
| + // neighbour.
|
| + if (predecessor_ordinal.IsValid() || successor_ordinal.IsValid()) {
|
| + syncer::StringOrdinal ordinal;
|
| + if (!predecessor_ordinal.IsValid()) {
|
| + // Only a successor.
|
| + ordinal = successor_ordinal.CreateBefore();
|
| + } else if (!successor_ordinal.IsValid()) {
|
| + // Only a predecessor.
|
| + ordinal = predecessor_ordinal.CreateAfter();
|
| + } else {
|
| + // Both a successor and predecessor
|
| + ordinal = predecessor_ordinal.CreateBetween(successor_ordinal);
|
| + }
|
| + EraseAppListOrdinal(moved_extension_id);
|
| + SetAppListOrdinal(moved_extension_id, ordinal);
|
| + UpdatePrefs(moved_extension_id, ordinal);
|
| + }
|
| +
|
| + FOR_EACH_OBSERVER(AppListExtensionSortingObserver, observers_,
|
| + OnAppListReordered());
|
| +}
|
| +
|
| +void AppListExtensionSorting::Erase(const std::string& extension_id) {
|
| + EraseAppListOrdinal(extension_id);
|
| + UpdatePrefs(extension_id, syncer::StringOrdinal());
|
| +}
|
| +
|
| +void AppListExtensionSorting::EraseAppListOrdinal(
|
| + const std::string& extension_id) {
|
| + if (!Contains(extension_id))
|
| + return;
|
| + AppListOrdinalMap::iterator ordinal_map_it =
|
| + app_list_ordinal_map_.find(GetAppListOrdinalFromPrefs(extension_id));
|
| + if (ordinal_map_it == app_list_ordinal_map_.end())
|
| + NOTREACHED();
|
| + ordinal_map_it->second.erase(extension_id);
|
| + if (ordinal_map_it->second.empty())
|
| + app_list_ordinal_map_.erase(ordinal_map_it);
|
| +}
|
| +
|
| +bool AppListExtensionSorting::ExtensionPrecedes(
|
| + const std::string& extension_id1,
|
| + const std::string& extension_id2) {
|
| + DCHECK(Contains(extension_id1) && Contains(extension_id2));
|
| + return GetAppListOrdinalFromPrefs(extension_id1).LessThan(
|
| + GetAppListOrdinalFromPrefs(extension_id2));
|
| +}
|
| +
|
| +bool AppListExtensionSorting::Contains(const std::string& extension_id) {
|
| + if (!GetAppListOrdinalFromPrefs(extension_id).IsValid())
|
| + return false;
|
| +
|
| + AppListOrdinalMap::iterator ordinal_map_it =
|
| + app_list_ordinal_map_.find(GetAppListOrdinalFromPrefs(extension_id));
|
| + if (ordinal_map_it == app_list_ordinal_map_.end())
|
| + return false;
|
| + return ordinal_map_it->second.count(extension_id) > 0;
|
| +}
|
| +
|
| +syncer::StringOrdinal AppListExtensionSorting::GetAppListOrdinalFromPrefs(
|
| + const std::string& extension_id) {
|
| + std::string raw_data;
|
| + // If the preference read fails then raw_data will still be unset and we will
|
| + // return an invalid StringOrdinal to signal that no app list ordinal was
|
| + // found.
|
| + extension_scoped_prefs_->ReadPrefAsString(
|
| + extension_id, kPrefAppListOrdinal, &raw_data);
|
| + return syncer::StringOrdinal(raw_data);
|
| +}
|
| +
|
| +void AppListExtensionSorting::UpdateAppListOrdinalFromSync(
|
| + const std::string& extension_id,
|
| + const syncer::StringOrdinal& ordinal) {
|
| + EraseAppListOrdinal(extension_id);
|
| + SetAppListOrdinal(extension_id, ordinal);
|
| + UpdatePrefs(extension_id, ordinal);
|
| +}
|
| +
|
| +void AppListExtensionSorting::SetAppListOrdinal(
|
| + const std::string& extension_id,
|
| + const syncer::StringOrdinal& ordinal) {
|
| + app_list_ordinal_map_[ordinal].insert(extension_id);
|
| +}
|
| +
|
| +void AppListExtensionSorting::UpdatePrefs(
|
| + const std::string& extension_id,
|
| + const syncer::StringOrdinal& ordinal) {
|
| + if (ordinal.EqualsOrBothInvalid(GetAppListOrdinalFromPrefs(extension_id)))
|
| + return;
|
| +
|
| + Value* new_value = ordinal.IsValid() ?
|
| + Value::CreateStringValue(ordinal.ToInternalValue()) :
|
| + NULL;
|
| + extension_scoped_prefs_->UpdateExtensionPref(
|
| + extension_id,
|
| + kPrefAppListOrdinal,
|
| + new_value);
|
| +
|
| + if (!extension_service_)
|
| + return;
|
| +
|
| + // Sync extension pref.
|
| + const extensions::Extension* extension =
|
| + extension_service_->GetInstalledExtension(extension_id);
|
| + if (extension)
|
| + extension_service_->SyncExtensionChangeIfNeeded(*extension);
|
| +}
|
| +
|
| +void AppListExtensionSorting::SetExtensionService(
|
| + ExtensionServiceInterface* extension_service) {
|
| + extension_service_ = extension_service;
|
| + for (size_t i = 0; i < default_initialized_extensions_.size(); ++i) {
|
| + const extensions::Extension* extension =
|
| + extension_service_->GetInstalledExtension(
|
| + default_initialized_extensions_[i]);
|
| + if (extension)
|
| + extension_service_->SyncExtensionChangeIfNeeded(*extension);
|
| + }
|
| + default_initialized_extensions_.clear();
|
| +}
|
| +
|
| +void AppListExtensionSorting::AddObserver(
|
| + AppListExtensionSortingObserver* observer) {
|
| + observers_.AddObserver(observer);
|
| +}
|
| +
|
| +void AppListExtensionSorting::RemoveObserver(
|
| + AppListExtensionSortingObserver* observer) {
|
| + observers_.RemoveObserver(observer);
|
| +}
|
|
|