summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcsharp@chromium.org <csharp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-12 20:08:23 +0000
committercsharp@chromium.org <csharp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-12 20:08:23 +0000
commit3569b5016fc94e8ca85060a7e4d7f6ca6ad0b1b5 (patch)
tree21832bdad689f5b74110515929cfec1845eaabe6
parent5d98aa9e354cc1607caabe1e6be49baeb36c6e54 (diff)
downloadchromium_src-3569b5016fc94e8ca85060a7e4d7f6ca6ad0b1b5.zip
chromium_src-3569b5016fc94e8ca85060a7e4d7f6ca6ad0b1b5.tar.gz
chromium_src-3569b5016fc94e8ca85060a7e4d7f6ca6ad0b1b5.tar.bz2
Moving Ordinal Specific Code out of ExtensionPrefs
Moved all of the ordinal specific code in ExtensionPrefs to a new class ExtensionSorting, also moved the related unit tests to an ExtensionSorting unit test file. There are no logic changes in this patch. BUG=104973 TEST=The icons on the NTP should behave the same as before, with users able to move them around and remember position. Review URL: http://codereview.chromium.org/9021034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117490 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_prefs.cc343
-rw-r--r--chrome/browser/extensions/extension_prefs.h157
-rw-r--r--chrome/browser/extensions/extension_prefs_unittest.cc560
-rw-r--r--chrome/browser/extensions/extension_prefs_unittest.h92
-rw-r--r--chrome/browser/extensions/extension_scoped_prefs.h43
-rw-r--r--chrome/browser/extensions/extension_service.cc19
-rw-r--r--chrome/browser/extensions/extension_sorting.cc364
-rw-r--r--chrome/browser/extensions/extension_sorting.h127
-rw-r--r--chrome/browser/extensions/extension_sorting_unittest.cc306
-rw-r--r--chrome/browser/ui/views/aura/app_list/extension_app_item.cc24
-rw-r--r--chrome/browser/ui/webui/ntp/app_launcher_handler.cc43
-rw-r--r--chrome/chrome_browser.gypi3
-rw-r--r--chrome/chrome_tests.gypi2
13 files changed, 1159 insertions, 924 deletions
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 493ba5a..2bc2dd1 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -8,6 +8,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_pref_store.h"
+#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/prefs/pref_notifier.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/common/chrome_notification_types.h"
@@ -22,10 +23,6 @@ using base::Time;
namespace {
-// The number of apps per page. This isn't a hard limit, but new apps installed
-// from the webstore will overflow onto a new page if this limit is reached.
-const int kNaturalAppPageSize = 18;
-
// Additional preferences keys
// Where an extension was installed from. (see Extension::Location)
@@ -110,14 +107,6 @@ const char kWebStoreLogin[] = "extensions.webstore_login";
// used for apps.
const char kPrefLaunchType[] = "launchType";
-// A preference determining the order of which the apps appear on the NTP.
-const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
-const char kPrefAppLaunchOrdinal[] = "app_launcher_ordinal";
-
-// A preference determining the page on which an app appears in the NTP.
-const char kPrefPageIndexDeprecated[] = "page_index";
-const char kPrefPageOrdinal[] = "page_ordinal";
-
// A preference specifying if the user dragged the app on the NTP.
const char kPrefUserDraggedApp[] = "user_dragged_app_ntp";
@@ -245,6 +234,8 @@ ExtensionPrefs::ExtensionPrefs(
: prefs_(prefs),
install_directory_(root_dir),
extension_pref_value_map_(extension_pref_value_map),
+ ALLOW_THIS_IN_INITIALIZER_LIST(extension_sorting_(
+ new ExtensionSorting(this, prefs))),
content_settings_store_(new ExtensionContentSettingsStore()) {
}
@@ -390,7 +381,7 @@ bool ExtensionPrefs::ReadIntegerFromPref(
bool ExtensionPrefs::ReadExtensionPrefInteger(
const std::string& extension_id, const std::string& pref_key,
- int* out_value) {
+ int* out_value) const {
const DictionaryValue* ext = GetExtensionPref(extension_id);
if (!ext) {
// No such extension yet.
@@ -401,7 +392,7 @@ bool ExtensionPrefs::ReadExtensionPrefInteger(
bool ExtensionPrefs::ReadExtensionPrefList(
const std::string& extension_id, const std::string& pref_key,
- const ListValue** out_value) {
+ const ListValue** out_value) const {
const DictionaryValue* ext = GetExtensionPref(extension_id);
ListValue* out = NULL;
if (!ext || !ext->GetList(pref_key, &out))
@@ -866,99 +857,6 @@ void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) {
}
}
-void ExtensionPrefs::MigrateAppIndex(const ExtensionIdSet& extension_ids) {
- if (extension_ids.empty())
- return;
-
- // Convert all the page index values to page ordinals. If there are any
- // app launch values that need to be migrated, inserted them into a sorted
- // set to be dealt with later.
- typedef std::map<StringOrdinal, std::map<int, const std::string*>,
- StringOrdinalLessThan> AppPositionToIdMapping;
- AppPositionToIdMapping app_launches_to_convert;
- for (ExtensionIdSet::const_iterator ext_id = extension_ids.begin();
- ext_id != extension_ids.end(); ++ext_id) {
- int old_page_index = 0;
- StringOrdinal page = GetPageOrdinal(*ext_id);
- if (ReadExtensionPrefInteger(*ext_id,
- kPrefPageIndexDeprecated,
- &old_page_index)) {
- // Some extensions have invalid page index, so we don't
- // attempt to convert them.
- if (old_page_index < 0) {
- DLOG(WARNING) << "Extension " << *ext_id
- << " has an invalid page index " << old_page_index
- << ". Aborting attempt to convert its index.";
- break;
- }
-
- // Since we require all earlier StringOrdinals to already exist in order
- // to properly convert from integers and we are iterating though them in
- // no given order, we create earlier StringOrdinal values as required.
- // This should be filled in by the time we are done with this loop.
- if (page_ordinal_map_.empty())
- page_ordinal_map_[StringOrdinal::CreateInitialOrdinal()] = 0;
- while (page_ordinal_map_.size() <= static_cast<size_t>(old_page_index)) {
- StringOrdinal earlier_page =
- page_ordinal_map_.rbegin()->first.CreateAfter();
- page_ordinal_map_[earlier_page] = 0;
- }
-
- page = PageIntegerAsStringOrdinal(old_page_index);
- SetPageOrdinal(*ext_id, page);
- UpdateExtensionPref(*ext_id, kPrefPageIndexDeprecated, NULL);
- }
-
- int old_app_launch_index = 0;
- if (ReadExtensionPrefInteger(*ext_id,
- kPrefAppLaunchIndexDeprecated,
- &old_app_launch_index)) {
- // We can't update the app launch index value yet, because we use
- // GetNextAppLaunchOrdinal to get the new ordinal value and it requires
- // all the ordinals with lower values to have already been migrated.
- // A valid page ordinal is also required because otherwise there is
- // no page to add the app to.
- if (page.IsValid())
- app_launches_to_convert[page][old_app_launch_index] = &*ext_id;
-
- UpdateExtensionPref(*ext_id, kPrefAppLaunchIndexDeprecated, NULL);
- }
- }
-
- // Remove any empty pages that may have been added. This shouldn't occur,
- // but double check here to prevent future problems with conversions between
- // integers and StringOrdinals.
- for (PageOrdinalMap::iterator it = page_ordinal_map_.begin();
- it != page_ordinal_map_.end();) {
- if (it->second == 0) {
- PageOrdinalMap::iterator prev_it = it;
- ++it;
- page_ordinal_map_.erase(prev_it);
- } else {
- ++it;
- }
- }
-
- if (app_launches_to_convert.empty())
- return;
-
- // Create the new app launch ordinals and remove the old preferences. Since
- // the set is sorted, each time we migrate an apps index, we know that all of
- // the remaining apps will appear further down the NTP than it or on a
- // different page.
- for (AppPositionToIdMapping::const_iterator page_it =
- app_launches_to_convert.begin();
- page_it != app_launches_to_convert.end(); ++page_it) {
- StringOrdinal page = page_it->first;
- for (std::map<int, const std::string*>::const_iterator launch_it =
- page_it->second.begin(); launch_it != page_it->second.end();
- ++launch_it) {
- SetAppLaunchOrdinal(*(launch_it->second),
- CreateNextAppLaunchOrdinal(page));
- }
- }
-}
-
ExtensionPermissionSet* ExtensionPrefs::GetGrantedPermissions(
const std::string& extension_id) {
CHECK(Extension::IdIsValid(extension_id));
@@ -1206,9 +1104,12 @@ void ExtensionPrefs::OnExtensionInstalled(
if (extension->is_app()) {
StringOrdinal new_page_ordinal =
- page_ordinal.IsValid() ? page_ordinal : GetNaturalAppPageOrdinal();
- SetPageOrdinal(id, new_page_ordinal);
- SetAppLaunchOrdinal(id, CreateNextAppLaunchOrdinal(new_page_ordinal));
+ page_ordinal.IsValid() ? page_ordinal
+ : extension_sorting_->GetNaturalAppPageOrdinal();
+ extension_sorting_->SetPageOrdinal(id, new_page_ordinal);
+ extension_sorting_->SetAppLaunchOrdinal(
+ id,
+ extension_sorting_->CreateNextAppLaunchOrdinal(new_page_ordinal));
}
extension_pref_value_map_->RegisterExtension(
@@ -1235,37 +1136,6 @@ void ExtensionPrefs::OnExtensionUninstalled(const std::string& extension_id,
}
}
-void ExtensionPrefs::OnExtensionMoved(
- const std::string& moved_extension_id,
- const std::string& predecessor_extension_id,
- const std::string& successor_extension_id) {
- // If there are no neighbors then no changes are required, so we can return.
- if (predecessor_extension_id.empty() && successor_extension_id.empty())
- return;
-
- if (predecessor_extension_id.empty()) {
- SetAppLaunchOrdinal(
- moved_extension_id,
- GetAppLaunchOrdinal(successor_extension_id).CreateBefore());
- } else if (successor_extension_id.empty()) {
- SetAppLaunchOrdinal(
- moved_extension_id,
- GetAppLaunchOrdinal(predecessor_extension_id).CreateAfter());
- } else {
- const StringOrdinal& predecessor_ordinal =
- GetAppLaunchOrdinal(predecessor_extension_id);
- const StringOrdinal& successor_ordinal =
- GetAppLaunchOrdinal(successor_extension_id);
- SetAppLaunchOrdinal(moved_extension_id,
- predecessor_ordinal.CreateBetween(successor_ordinal));
- }
-
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
- content::Source<ExtensionPrefs>(this),
- content::NotificationService::NoDetails());
-}
-
void ExtensionPrefs::SetExtensionState(const std::string& extension_id,
Extension::State state) {
UpdateExtensionPref(extension_id, kPrefState,
@@ -1569,192 +1439,6 @@ void ExtensionPrefs::SetWebStoreLogin(const std::string& login) {
prefs_->SetString(kWebStoreLogin, login);
}
-StringOrdinal ExtensionPrefs::GetMinOrMaxAppLaunchOrdinalsOnPage(
- const StringOrdinal& target_page_ordinal,
- AppLaunchOrdinalReturn return_type) const {
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- CHECK(extensions);
- CHECK(target_page_ordinal.IsValid());
-
- StringOrdinal return_value;
- for (DictionaryValue::key_iterator ext_it = extensions->begin_keys();
- ext_it != extensions->end_keys(); ++ext_it) {
- const StringOrdinal& page_ordinal = GetPageOrdinal(*ext_it);
- if (page_ordinal.IsValid() && page_ordinal.Equal(target_page_ordinal)) {
- const StringOrdinal& app_launch_ordinal = GetAppLaunchOrdinal(*ext_it);
- if (app_launch_ordinal.IsValid()) {
- if (!return_value.IsValid())
- return_value = app_launch_ordinal;
- else if (return_type == ExtensionPrefs::MIN_ORDINAL &&
- app_launch_ordinal.LessThan(return_value))
- return_value = app_launch_ordinal;
- else if (return_type == ExtensionPrefs::MAX_ORDINAL &&
- app_launch_ordinal.GreaterThan(return_value))
- return_value = app_launch_ordinal;
- }
- }
- }
-
- return return_value;
-}
-
-StringOrdinal ExtensionPrefs::GetAppLaunchOrdinal(
- const std::string& extension_id) const {
- std::string raw_value;
- // If the preference read fails then raw_value will still be unset and we
- // will return an invalid StringOrdinal to signal that no app launch ordinal
- // was found.
- ReadExtensionPrefString(extension_id, kPrefAppLaunchOrdinal, &raw_value);
- return StringOrdinal(raw_value);
-}
-
-void ExtensionPrefs::SetAppLaunchOrdinal(const std::string& extension_id,
- const StringOrdinal& ordinal) {
- UpdateExtensionPref(extension_id, kPrefAppLaunchOrdinal,
- Value::CreateStringValue(ordinal.ToString()));
-}
-
-StringOrdinal ExtensionPrefs::CreateFirstAppLaunchOrdinal(
- const StringOrdinal& page_ordinal) const {
- const StringOrdinal& min_ordinal =
- GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
- ExtensionPrefs::MIN_ORDINAL);
-
- if (min_ordinal.IsValid())
- return min_ordinal.CreateBefore();
- else
- return StringOrdinal::CreateInitialOrdinal();
-}
-
-StringOrdinal ExtensionPrefs::CreateNextAppLaunchOrdinal(
- const StringOrdinal& page_ordinal) const {
- const StringOrdinal& max_ordinal =
- GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
- ExtensionPrefs::MAX_ORDINAL);
-
- if (max_ordinal.IsValid())
- return max_ordinal.CreateAfter();
- else
- return StringOrdinal::CreateInitialOrdinal();
-}
-
-StringOrdinal ExtensionPrefs::CreateFirstAppPageOrdinal() const {
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- CHECK(extensions);
-
- if (page_ordinal_map_.empty())
- return StringOrdinal::CreateInitialOrdinal();
-
- return page_ordinal_map_.begin()->first;
-}
-
-StringOrdinal ExtensionPrefs::GetNaturalAppPageOrdinal() const {
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- CHECK(extensions);
-
- if (page_ordinal_map_.empty())
- return StringOrdinal::CreateInitialOrdinal();
-
- for (PageOrdinalMap::const_iterator it = page_ordinal_map_.begin();
- it != page_ordinal_map_.end(); ++it) {
- if (it->second < kNaturalAppPageSize)
- return it->first;
- }
-
- // Add a new page as all existing pages are full.
- StringOrdinal last_element = page_ordinal_map_.rbegin()->first;
- return last_element.CreateAfter();
-}
-
-StringOrdinal ExtensionPrefs::GetPageOrdinal(const std::string& extension_id)
- const {
- 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 page ordinal was found.
- ReadExtensionPrefString(extension_id, kPrefPageOrdinal, &raw_data);
- return StringOrdinal(raw_data);
-}
-
-void ExtensionPrefs::SetPageOrdinal(const std::string& extension_id,
- const StringOrdinal& page_ordinal) {
- UpdatePageOrdinalMap(GetPageOrdinal(extension_id), page_ordinal);
- UpdateExtensionPref(extension_id, kPrefPageOrdinal,
- Value::CreateStringValue(page_ordinal.ToString()));
-}
-
-void ExtensionPrefs::ClearPageOrdinal(const std::string& extension_id) {
- UpdatePageOrdinalMap(GetPageOrdinal(extension_id), StringOrdinal());
- UpdateExtensionPref(extension_id, kPrefPageOrdinal, NULL);
-}
-
-void ExtensionPrefs::InitializePageOrdinalMap(
- const ExtensionIdSet& extension_ids) {
- for (ExtensionIdSet::const_iterator ext_it = extension_ids.begin();
- ext_it != extension_ids.end(); ++ext_it) {
- UpdatePageOrdinalMap(StringOrdinal(), GetPageOrdinal(*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);
- }
-
- // Include the Web Store App since it is displayed on the NTP.
- StringOrdinal web_store_app_page =
- GetPageOrdinal(extension_misc::kWebStoreAppId);
- if (web_store_app_page.IsValid())
- UpdatePageOrdinalMap(StringOrdinal(), web_store_app_page);
-}
-
-void ExtensionPrefs::UpdatePageOrdinalMap(const StringOrdinal& old_value,
- const StringOrdinal& new_value) {
- if (new_value.IsValid())
- ++page_ordinal_map_[new_value];
-
- if (old_value.IsValid()) {
- --page_ordinal_map_[old_value];
-
- if (page_ordinal_map_[old_value] == 0)
- page_ordinal_map_.erase(old_value);
- }
-}
-
-int ExtensionPrefs::PageStringOrdinalAsInteger(
- const StringOrdinal& page_ordinal) const {
- if (!page_ordinal.IsValid())
- return -1;
-
- PageOrdinalMap::const_iterator it = page_ordinal_map_.find(page_ordinal);
- if (it != page_ordinal_map_.end()) {
- return std::distance(page_ordinal_map_.begin(), it);
- } else {
- return -1;
- }
-}
-
-StringOrdinal ExtensionPrefs::PageIntegerAsStringOrdinal(size_t page_index)
- const {
- // We shouldn't have a page_index that is more than 1 position away from the
- // current end as that would imply we have empty pages which is not allowed.
- CHECK_LE(page_index, page_ordinal_map_.size());
-
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- if (!extensions)
- return StringOrdinal();
-
- if (page_index < page_ordinal_map_.size()) {
- PageOrdinalMap::const_iterator it = page_ordinal_map_.begin();
- std::advance(it, page_index);
-
- return it->first;
-
- } else {
- if (page_ordinal_map_.empty())
- return StringOrdinal::CreateInitialOrdinal();
- else
- return page_ordinal_map_.rbegin()->first.CreateAfter();
- }
-}
-
bool ExtensionPrefs::WasAppDraggedByUser(const std::string& extension_id) {
return ReadExtensionPrefBoolean(extension_id, kPrefUserDraggedApp);
}
@@ -1899,9 +1583,8 @@ void ExtensionPrefs::InitPrefStore(bool extensions_disabled) {
}
FixMissingPrefs(extension_ids);
- InitializePageOrdinalMap(extension_ids);
MigratePermissions(extension_ids);
- MigrateAppIndex(extension_ids);
+ extension_sorting_->MigrateAppIndex(extension_ids);
// Store extension controlled preference values in the
// |extension_pref_value_map_|, which then informs the subscribers
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 5573f18..75d1e32 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -14,12 +14,14 @@
#include "base/time.h"
#include "chrome/browser/extensions/extension_content_settings_store.h"
#include "chrome/browser/extensions/extension_prefs_scope.h"
+#include "chrome/browser/extensions/extension_scoped_prefs.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/string_ordinal.h"
#include "googleurl/src/gurl.h"
class ExtensionPrefValueMap;
+class ExtensionSorting;
class URLPatternSet;
// Class for managing global and per-extension preferences.
@@ -37,7 +39,8 @@ class URLPatternSet;
// preference. Extension-controlled preferences are stored in
// PrefValueStore::extension_prefs(), which this class populates and
// maintains as the underlying extensions change.
-class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
+class ExtensionPrefs : public ExtensionContentSettingsStore::Observer,
+ public ExtensionScopedPrefs {
public:
// Key name for a preference that keeps track of per-extension settings. This
// is a dictionary object read from the Preferences file, keyed off of
@@ -112,13 +115,6 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
const Extension::Location& location,
bool external_uninstall);
- // Updates the app launcher value for the moved extension so that it is now
- // located after the given predecessor and before the successor.
- // Empty strings are used to indicate no successor or predecessor.
- void OnExtensionMoved(const std::string& moved_extension_id,
- const std::string& predecessor_extension_id,
- const std::string& successor_extension_id);
-
// Called to change the extension's state when it is enabled/disabled.
void SetExtensionState(const std::string& extension_id, Extension::State);
@@ -313,56 +309,6 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
bool GetWebStoreLogin(std::string* result);
void SetWebStoreLogin(const std::string& login);
- // Get the application launch ordinal for an app with |extension_id|. This
- // determines the order in which the app appears on the page it's on in the
- // New Tab Page (Note that you can compare app launch ordinals only if the
- // apps are on the same page). A string value close to |a*| generally
- // indicates top left. If the extension has no launch ordinal, an invalid
- // StringOrdinal is returned.
- StringOrdinal GetAppLaunchOrdinal(const std::string& extension_id) const;
-
- // Sets a specific launch ordinal for an app with |extension_id|.
- void SetAppLaunchOrdinal(const std::string& extension_id,
- const StringOrdinal& ordinal);
-
- // Returns a StringOrdinal that is lower than any app launch ordinal for the
- // given page.
- StringOrdinal CreateFirstAppLaunchOrdinal(const StringOrdinal& page_ordinal)
- const;
-
- // Returns a StringOrdinal that is higher than any app launch ordinal for the
- // given page.
- StringOrdinal CreateNextAppLaunchOrdinal(const StringOrdinal& page_ordinal)
- const;
-
- // Returns a StringOrdinal that is lower than any existing page ordinal.
- StringOrdinal CreateFirstAppPageOrdinal() const;
-
- // Gets the page a new app should install to, which is the earliest non-full
- // page. The returned ordinal may correspond to a page that doesn't yet exist
- // if all pages are full.
- StringOrdinal GetNaturalAppPageOrdinal() const;
-
- // Get the page ordinal for an app with |extension_id|. This determines
- // which page an app will appear on in page-based NTPs. If the app has no
- // page specified, an invalid StringOrdinal is returned.
- StringOrdinal GetPageOrdinal(const std::string& extension_id) const;
-
- // Sets a specific page ordinal for an app with |extension_id|.
- void SetPageOrdinal(const std::string& extension_id,
- const StringOrdinal& ordinal);
-
- // Removes the page ordinal for an app.
- void ClearPageOrdinal(const std::string& extension_id);
-
- // Convert the page StringOrdinal value to its integer equivalent. This takes
- // O(# of apps) worst-case.
- int PageStringOrdinalAsInteger(const StringOrdinal& page_ordinal) const;
-
- // Converts the page index integer to its StringOrdinal equivalent. This takes
- // O(# of apps) worst-case.
- StringOrdinal PageIntegerAsStringOrdinal(size_t page_index) const;
-
// Returns true if the user repositioned the app on the app launcher via drag
// and drop.
bool WasAppDraggedByUser(const std::string& extension_id);
@@ -431,15 +377,17 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
// The underlying PrefService.
PrefService* pref_service() const { return prefs_; }
+ // The underlying ExtensionSorting.
+ ExtensionSorting* extension_sorting() const {
+ return extension_sorting_.get();
+ }
+
protected:
// For unit testing. Enables injecting an artificial clock that is used
// to query the current time, when an extension is installed.
virtual base::Time GetCurrentTime() const;
private:
- friend class ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage; // Unit test.
- friend class ExtensionPrefsMigrateAppIndex; // Unit test.
- friend class ExtensionPrefsMigrateAppIndexInvalid; // Unit test.
friend class ExtensionPrefsUninstallExtension; // Unit test.
// ExtensionContentSettingsStore::Observer methods:
@@ -447,6 +395,25 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
const std::string& extension_id,
bool incognito) OVERRIDE;
+ // ExtensionScopedPrefs methods:
+ virtual void UpdateExtensionPref(const std::string& id,
+ const std::string& key,
+ base::Value* value) OVERRIDE;
+ virtual void DeleteExtensionPrefs(const std::string& id) OVERRIDE;
+ virtual bool ReadExtensionPrefBoolean(
+ const std::string& extension_id,
+ const std::string& pref_key) const OVERRIDE;
+ virtual bool ReadExtensionPrefInteger(const std::string& extension_id,
+ const std::string& pref_key,
+ int* out_value) const OVERRIDE;
+ virtual bool ReadExtensionPrefList(
+ const std::string& extension_id,
+ const std::string& pref_key,
+ const base::ListValue** out_value) const OVERRIDE;
+ virtual bool ReadExtensionPrefString(const std::string& extension_id,
+ const std::string& pref_key,
+ std::string* out_value) const OVERRIDE;
+
// Converts absolute paths in the pref to paths relative to the
// install_directory_.
void MakePathsRelative();
@@ -455,44 +422,17 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
// consumers who expect full paths.
void MakePathsAbsolute(base::DictionaryValue* dict);
- // Sets the pref |key| for extension |id| to |value|.
- void UpdateExtensionPref(const std::string& id,
- const std::string& key,
- base::Value* value);
-
- // Deletes the pref dictionary for extension |id|.
- void DeleteExtensionPrefs(const std::string& id);
-
// Reads a boolean pref from |ext| with key |pref_key|.
// Return false if the value is false or |pref_key| does not exist.
static bool ReadBooleanFromPref(const base::DictionaryValue* ext,
const std::string& pref_key);
- // Reads a boolean pref |pref_key| from extension with id |extension_id|.
- bool ReadExtensionPrefBoolean(const std::string& extension_id,
- const std::string& pref_key) const;
-
// Reads an integer pref from |ext| with key |pref_key|.
// Return false if the value does not exist.
static bool ReadIntegerFromPref(const base::DictionaryValue* ext,
const std::string& pref_key,
int* out_value);
- // Reads an integer pref |pref_key| from extension with id |extension_id|.
- bool ReadExtensionPrefInteger(const std::string& extension_id,
- const std::string& pref_key,
- int* out_value);
-
- // Reads a list pref |pref_key| from extension with id |extension_id|.
- bool ReadExtensionPrefList(const std::string& extension_id,
- const std::string& pref_key,
- const base::ListValue** out_value);
-
- // Reads a string pref |pref_key| from extension with id |extension_id|.
- bool ReadExtensionPrefString(const std::string& extension_id,
- const std::string& pref_key,
- std::string* out_value) const;
-
// Interprets the list pref, |pref_key| in |extension_id|'s preferences, as a
// URLPatternSet. The |valid_schemes| specify how to parse the URLPatterns.
bool ReadExtensionPrefURLPatternSet(const std::string& extension_id,
@@ -545,55 +485,24 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer {
// Migrates the permissions data in the pref store.
void MigratePermissions(const ExtensionIdSet& extension_ids);
- // Migrates the app launcher and page index values.
- void MigrateAppIndex(const ExtensionIdSet& extension_ids);
-
// Checks whether there is a state pref for the extension and if so, whether
// it matches |check_state|.
bool DoesExtensionHaveState(const std::string& id,
Extension::State check_state) const;
- // An enum used by GetMinOrMaxAppLaunchOrdinalsOnPage to specify which
- // value should be returned.
- enum AppLaunchOrdinalReturn {MIN_ORDINAL, MAX_ORDINAL};
-
- // This function returns the lowest ordinal on |page_ordinal| if
- // |return_value| == AppLaunchOrdinalReturn::MIN_ORDINAL, otherwise it returns
- // the largest ordinal on |page_ordinal|. If there are no apps on the page
- // then an invalid StringOrdinal is returned. It is an error to call this
- // function with an invalid |page_ordinal|.
- StringOrdinal GetMinOrMaxAppLaunchOrdinalsOnPage(
- const StringOrdinal& page_ordinal,
- AppLaunchOrdinalReturn return_type) const;
-
- // Initialize the |page_ordinal_map_| with the page ordinals used by the
- // given extensions.
- void InitializePageOrdinalMap(const ExtensionIdSet& extension_ids);
-
- // Called when an application changes the value of its page ordinal so that
- // |page_ordinal_map_| is aware that |old_value| page ordinal has been
- // replace by the |new_value| page ordinal and adjusts its mapping
- // accordingly. This works with valid and invalid StringOrdinals.
- void UpdatePageOrdinalMap(const StringOrdinal& old_value,
- const StringOrdinal& new_value);
-
// The pref service specific to this set of extension prefs. Owned by profile.
PrefService* prefs_;
// Base extensions install directory.
FilePath install_directory_;
- // A map of all the StringOrdinal page indices mapping to how often they are
- // used, this is used for mapping the StringOrdinals to their integer
- // equivalent as well as quick lookup of the sorted StringOrdinals.
- // TODO(csharp) Convert this to a two-layer map to allow page ordinal lookup
- // by id and vice versa.
- typedef std::map<StringOrdinal, int, StringOrdinalLessThan> PageOrdinalMap;
- PageOrdinalMap page_ordinal_map_;
-
// Weak pointer, owned by Profile.
ExtensionPrefValueMap* extension_pref_value_map_;
+ // Contains all the logic for handling the order for various extension
+ // properties.
+ scoped_ptr<ExtensionSorting> extension_sorting_;
+
scoped_refptr<ExtensionContentSettingsStore> content_settings_store_;
DISALLOW_COPY_AND_ASSIGN(ExtensionPrefs);
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 66dfbfd..d43bd55 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -1,15 +1,15 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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 "base/message_loop.h"
+#include "extension_prefs_unittest.h"
+
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/stringprintf.h"
#include "chrome/browser/extensions/extension_prefs.h"
-#include "chrome/browser/extensions/test_extension_prefs.h"
#include "chrome/browser/extensions/extension_pref_value_map.h"
#include "chrome/browser/prefs/pref_change_registrar.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
@@ -20,8 +20,6 @@
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/test/notification_observer_mock.h"
-#include "content/test/test_browser_thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
using base::Time;
using base::TimeDelta;
@@ -47,52 +45,28 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
extent->AddPattern(URLPattern(schemes, pattern));
}
-// Base class for tests.
-class ExtensionPrefsTest : public testing::Test {
- public:
- ExtensionPrefsTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- file_thread_(BrowserThread::FILE, &message_loop_) {
- }
-
- // This function will get called once, and is the right place to do operations
- // on ExtensionPrefs that write data.
- virtual void Initialize() = 0;
-
- // This function will be called twice - once while the original ExtensionPrefs
- // object is still alive, and once after recreation. Thus, it tests that
- // things don't break after any ExtensionPrefs startup work.
- virtual void Verify() = 0;
-
- // This function is called to Register preference default values.
- virtual void RegisterPreferences() {}
-
- virtual void SetUp() {
- RegisterPreferences();
- Initialize();
- }
+ExtensionPrefsTest::ExtensionPrefsTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE, &message_loop_) {
+}
- virtual void TearDown() {
- Verify();
+ExtensionPrefsTest::~ExtensionPrefsTest() {}
- // Reset ExtensionPrefs, and re-verify.
- prefs_.RecreateExtensionPrefs();
- RegisterPreferences();
- Verify();
- }
+void ExtensionPrefsTest::RegisterPreferences() {}
- protected:
- ExtensionPrefs* prefs() { return prefs_.prefs(); }
-
- MessageLoop message_loop_;
- content::TestBrowserThread ui_thread_;
- content::TestBrowserThread file_thread_;
+void ExtensionPrefsTest::SetUp() {
+ RegisterPreferences();
+ Initialize();
+}
- TestExtensionPrefs prefs_;
+void ExtensionPrefsTest::TearDown() {
+ Verify();
- private:
- DISALLOW_COPY_AND_ASSIGN(ExtensionPrefsTest);
-};
+ // Reset ExtensionPrefs, and re-verify.
+ prefs_.RecreateExtensionPrefs();
+ RegisterPreferences();
+ Verify();
+}
// Tests the LastPingDay/SetLastPingDay functions.
class ExtensionPrefsLastPingDay : public ExtensionPrefsTest {
@@ -670,116 +644,6 @@ class ExtensionPrefsOnExtensionInstalled : public ExtensionPrefsTest {
TEST_F(ExtensionPrefsOnExtensionInstalled,
ExtensionPrefsOnExtensionInstalled) {}
-class ExtensionPrefsAppLaunchOrdinal : public ExtensionPrefsTest {
- public:
- virtual void Initialize() {
- // No extensions yet.
- StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
- EXPECT_TRUE(StringOrdinal::CreateInitialOrdinal().Equal(
- prefs()->CreateNextAppLaunchOrdinal(page)));
-
- extension_ = prefs_.AddApp("on_extension_installed");
- EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
- prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
- false, StringOrdinal());
- }
-
- virtual void Verify() {
- StringOrdinal launch_ordinal =
- prefs()->GetAppLaunchOrdinal(extension_->id());
- StringOrdinal page_ordinal = StringOrdinal::CreateInitialOrdinal();
-
- // Extension should have been assigned a valid StringOrdinal.
- EXPECT_TRUE(launch_ordinal.IsValid());
- EXPECT_TRUE(launch_ordinal.LessThan(
- prefs()->CreateNextAppLaunchOrdinal(page_ordinal)));
- // Set a new launch ordinal of and verify it comes after.
- prefs()->SetAppLaunchOrdinal(
- extension_->id(),
- prefs()->CreateNextAppLaunchOrdinal(page_ordinal));
- StringOrdinal new_launch_ordinal =
- prefs()->GetAppLaunchOrdinal(extension_->id());
- EXPECT_TRUE(launch_ordinal.LessThan(new_launch_ordinal));
-
- // This extension doesn't exist, so it should return an invalid
- // StringOrdinal.
- StringOrdinal invalid_app_launch_ordinal =
- prefs()->GetAppLaunchOrdinal("foo");
- EXPECT_FALSE(invalid_app_launch_ordinal.IsValid());
- EXPECT_EQ(-1, prefs()->PageStringOrdinalAsInteger(
- invalid_app_launch_ordinal));
-
- // The second page doesn't have any apps so its next launch ordinal should
- // be the first launch ordinal.
- StringOrdinal next_page = page_ordinal.CreateAfter();
- StringOrdinal next_page_app_launch_ordinal =
- prefs()->CreateNextAppLaunchOrdinal(next_page);
- EXPECT_TRUE(next_page_app_launch_ordinal.Equal(
- prefs()->CreateFirstAppLaunchOrdinal(next_page)));
- }
-
- private:
- scoped_refptr<Extension> extension_;
-};
-TEST_F(ExtensionPrefsAppLaunchOrdinal, ExtensionPrefsAppLaunchOrdinal) {}
-
-class ExtensionPrefsPageOrdinal : public ExtensionPrefsTest {
- public:
- virtual void Initialize() {
- extension_ = prefs_.AddApp("page_ordinal");
- // Install with a page preference.
- StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
- prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
- false, page);
- EXPECT_TRUE(page.Equal(prefs()->GetPageOrdinal(extension_->id())));
- EXPECT_EQ(0, prefs()->PageStringOrdinalAsInteger(page));
-
- scoped_refptr<Extension> extension2 = prefs_.AddApp("page_ordinal_2");
- // Install without any page preference.
- prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
- false, StringOrdinal());
- EXPECT_TRUE(prefs()->GetPageOrdinal(extension_->id()).IsValid());
- }
-
- virtual void Verify() {
- StringOrdinal old_page = prefs()->GetPageOrdinal(extension_->id());
- StringOrdinal new_page = old_page.CreateAfter();
-
- // Set the page ordinal.
- prefs()->SetPageOrdinal(extension_->id(), new_page);
- // Verify the page ordinal.
- EXPECT_TRUE(new_page.Equal(prefs()->GetPageOrdinal(extension_->id())));
- EXPECT_EQ(1, prefs()->PageStringOrdinalAsInteger(new_page));
-
- // This extension doesn't exist, so it should return an invalid
- // StringOrdinal.
- EXPECT_FALSE(prefs()->GetPageOrdinal("foo").IsValid());
- }
-
- private:
- scoped_refptr<Extension> extension_;
-};
-TEST_F(ExtensionPrefsPageOrdinal, ExtensionPrefsPageOrdinal) {}
-
-class ExtensionPrefsAppLocation : public ExtensionPrefsTest {
- public:
- virtual void Initialize() {
- extension_ = prefs_.AddExtension("not_an_app");
- // Non-apps should not have any app launch ordinal or page ordinal.
- prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
- false, StringOrdinal());
- }
-
- virtual void Verify() {
- EXPECT_FALSE(prefs()->GetAppLaunchOrdinal(extension_->id()).IsValid());
- EXPECT_FALSE(prefs()->GetPageOrdinal(extension_->id()).IsValid());
- }
-
- private:
- scoped_refptr<Extension> extension_;
-};
-TEST_F(ExtensionPrefsAppLocation, ExtensionPrefsAppLocation) {}
-
class ExtensionPrefsAppDraggedByUser : public ExtensionPrefsTest {
public:
virtual void Initialize() {
@@ -842,126 +706,116 @@ TEST_F(ExtensionPrefsFlags, ExtensionPrefsFlags) {}
namespace keys = extension_manifest_keys;
-class ExtensionPrefsPreferencesBase : public ExtensionPrefsTest {
- public:
- ExtensionPrefsPreferencesBase()
- : ExtensionPrefsTest(),
- ext1_(NULL),
- ext2_(NULL),
- ext3_(NULL),
- installed() {
- DictionaryValue simple_dict;
- std::string error;
-
- simple_dict.SetString(keys::kVersion, "1.0.0.0");
- simple_dict.SetString(keys::kName, "unused");
-
- ext1_scoped_ = Extension::Create(
- prefs_.temp_dir().AppendASCII("ext1_"), Extension::EXTERNAL_PREF,
- simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
- ext2_scoped_ = Extension::Create(
- prefs_.temp_dir().AppendASCII("ext2_"), Extension::EXTERNAL_PREF,
- simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
- ext3_scoped_ = Extension::Create(
- prefs_.temp_dir().AppendASCII("ext3_"), Extension::EXTERNAL_PREF,
- simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
-
- ext1_ = ext1_scoped_.get();
- ext2_ = ext2_scoped_.get();
- ext3_ = ext3_scoped_.get();
-
- for (size_t i = 0; i < arraysize(installed); ++i)
- installed[i] = false;
- }
-
- void RegisterPreferences() {
- prefs()->pref_service()->RegisterStringPref(kPref1,
- kDefaultPref1,
- PrefService::UNSYNCABLE_PREF);
- prefs()->pref_service()->RegisterStringPref(kPref2,
- kDefaultPref2,
- PrefService::UNSYNCABLE_PREF);
- prefs()->pref_service()->RegisterStringPref(kPref3,
- kDefaultPref3,
- PrefService::UNSYNCABLE_PREF);
- prefs()->pref_service()->RegisterStringPref(kPref4,
- kDefaultPref4,
- PrefService::UNSYNCABLE_PREF);
- }
-
- void InstallExtControlledPref(Extension *ext,
- const std::string& key,
- Value* val) {
- EnsureExtensionInstalled(ext);
- prefs()->SetExtensionControlledPref(
- ext->id(), key, kExtensionPrefsScopeRegular, val);
- }
+ExtensionPrefsPrepopulatedTest::ExtensionPrefsPrepopulatedTest()
+ : ExtensionPrefsTest(),
+ ext1_(NULL),
+ ext2_(NULL),
+ ext3_(NULL),
+ installed() {
+ DictionaryValue simple_dict;
+ std::string error;
+
+ simple_dict.SetString(keys::kVersion, "1.0.0.0");
+ simple_dict.SetString(keys::kName, "unused");
+
+ ext1_scoped_ = Extension::Create(
+ prefs_.temp_dir().AppendASCII("ext1_"), Extension::EXTERNAL_PREF,
+ simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
+ ext2_scoped_ = Extension::Create(
+ prefs_.temp_dir().AppendASCII("ext2_"), Extension::EXTERNAL_PREF,
+ simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
+ ext3_scoped_ = Extension::Create(
+ prefs_.temp_dir().AppendASCII("ext3_"), Extension::EXTERNAL_PREF,
+ simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
+
+ ext1_ = ext1_scoped_.get();
+ ext2_ = ext2_scoped_.get();
+ ext3_ = ext3_scoped_.get();
+
+ for (size_t i = 0; i < arraysize(installed); ++i)
+ installed[i] = false;
+}
- void InstallExtControlledPrefIncognito(Extension *ext,
- const std::string& key,
- Value* val) {
- EnsureExtensionInstalled(ext);
- prefs()->SetExtensionControlledPref(
- ext->id(), key, kExtensionPrefsScopeIncognitoPersistent, val);
- }
+ExtensionPrefsPrepopulatedTest::~ExtensionPrefsPrepopulatedTest() {}
+
+void ExtensionPrefsPrepopulatedTest::RegisterPreferences() {
+ prefs()->pref_service()->RegisterStringPref(kPref1,
+ kDefaultPref1,
+ PrefService::UNSYNCABLE_PREF);
+ prefs()->pref_service()->RegisterStringPref(kPref2,
+ kDefaultPref2,
+ PrefService::UNSYNCABLE_PREF);
+ prefs()->pref_service()->RegisterStringPref(kPref3,
+ kDefaultPref3,
+ PrefService::UNSYNCABLE_PREF);
+ prefs()->pref_service()->RegisterStringPref(kPref4,
+ kDefaultPref4,
+ PrefService::UNSYNCABLE_PREF);
+}
- void InstallExtControlledPrefIncognitoSessionOnly(
- Extension *ext,
- const std::string& key,
- Value* val) {
- EnsureExtensionInstalled(ext);
- prefs()->SetExtensionControlledPref(
- ext->id(), key, kExtensionPrefsScopeIncognitoSessionOnly, val);
- }
+void ExtensionPrefsPrepopulatedTest::InstallExtControlledPref(
+ Extension *ext,
+ const std::string& key,
+ Value* val) {
+ EnsureExtensionInstalled(ext);
+ prefs()->SetExtensionControlledPref(
+ ext->id(), key, kExtensionPrefsScopeRegular, val);
+}
- void InstallExtension(Extension *ext) {
- EnsureExtensionInstalled(ext);
- }
+void ExtensionPrefsPrepopulatedTest::InstallExtControlledPrefIncognito(
+ Extension *ext,
+ const std::string& key,
+ Value* val) {
+ EnsureExtensionInstalled(ext);
+ prefs()->SetExtensionControlledPref(
+ ext->id(), key, kExtensionPrefsScopeIncognitoPersistent, val);
+}
- void UninstallExtension(const std::string& extension_id) {
- EnsureExtensionUninstalled(extension_id);
- }
+void ExtensionPrefsPrepopulatedTest
+::InstallExtControlledPrefIncognitoSessionOnly(Extension *ext,
+ const std::string& key,
+ Value* val) {
+ EnsureExtensionInstalled(ext);
+ prefs()->SetExtensionControlledPref(
+ ext->id(), key, kExtensionPrefsScopeIncognitoSessionOnly, val);
+}
- // Weak references, for convenience.
- Extension* ext1_;
- Extension* ext2_;
- Extension* ext3_;
+void ExtensionPrefsPrepopulatedTest::InstallExtension(Extension *ext) {
+ EnsureExtensionInstalled(ext);
+}
- // Flags indicating whether each of the extensions has been installed, yet.
- bool installed[3];
+void ExtensionPrefsPrepopulatedTest::UninstallExtension(
+ const std::string& extension_id) {
+ EnsureExtensionUninstalled(extension_id);
+}
- private:
- void EnsureExtensionInstalled(Extension *ext) {
- // Install extension the first time a preference is set for it.
- Extension* extensions[] = {ext1_, ext2_, ext3_};
- for (int i = 0; i < 3; ++i) {
- if (ext == extensions[i] && !installed[i]) {
- prefs()->OnExtensionInstalled(ext, Extension::ENABLED,
- false, StringOrdinal());
- installed[i] = true;
- break;
- }
+void ExtensionPrefsPrepopulatedTest::EnsureExtensionInstalled(Extension *ext) {
+ // Install extension the first time a preference is set for it.
+ Extension* extensions[] = {ext1_, ext2_, ext3_};
+ for (int i = 0; i < 3; ++i) {
+ if (ext == extensions[i] && !installed[i]) {
+ prefs()->OnExtensionInstalled(ext, Extension::ENABLED,
+ false, StringOrdinal());
+ installed[i] = true;
+ break;
}
}
+}
- void EnsureExtensionUninstalled(const std::string& extension_id) {
- Extension* extensions[] = {ext1_, ext2_, ext3_};
- for (int i = 0; i < 3; ++i) {
- if (extensions[i]->id() == extension_id) {
- installed[i] = false;
- break;
- }
+void ExtensionPrefsPrepopulatedTest::EnsureExtensionUninstalled(
+ const std::string& extension_id) {
+ Extension* extensions[] = {ext1_, ext2_, ext3_};
+ for (int i = 0; i < 3; ++i) {
+ if (extensions[i]->id() == extension_id) {
+ installed[i] = false;
+ break;
}
- prefs()->OnExtensionUninstalled(extension_id, Extension::INTERNAL, false);
}
-
- scoped_refptr<Extension> ext1_scoped_;
- scoped_refptr<Extension> ext2_scoped_;
- scoped_refptr<Extension> ext3_scoped_;
-};
+ prefs()->OnExtensionUninstalled(extension_id, Extension::INTERNAL, false);
+}
class ExtensionPrefsInstallOneExtension
- : public ExtensionPrefsPreferencesBase {
+ : public ExtensionPrefsPrepopulatedTest {
virtual void Initialize() {
InstallExtControlledPref(ext1_, kPref1, Value::CreateStringValue("val1"));
}
@@ -974,7 +828,7 @@ TEST_F(ExtensionPrefsInstallOneExtension, ExtensionPrefsInstallOneExtension) {}
// Check that we do not forget persistent incognito values after a reload.
class ExtensionPrefsInstallIncognitoPersistent
- : public ExtensionPrefsPreferencesBase {
+ : public ExtensionPrefsPrepopulatedTest {
public:
virtual void Initialize() {
InstallExtControlledPref(ext1_, kPref1, Value::CreateStringValue("val1"));
@@ -999,7 +853,7 @@ TEST_F(ExtensionPrefsInstallIncognitoPersistent,
// Check that we forget 'session only' incognito values after a reload.
class ExtensionPrefsInstallIncognitoSessionOnly
- : public ExtensionPrefsPreferencesBase {
+ : public ExtensionPrefsPrepopulatedTest {
public:
ExtensionPrefsInstallIncognitoSessionOnly() : iteration_(0) {}
@@ -1032,8 +886,7 @@ class ExtensionPrefsInstallIncognitoSessionOnly
TEST_F(ExtensionPrefsInstallIncognitoSessionOnly,
ExtensionPrefsInstallOneExtension) {}
-class ExtensionPrefsUninstallExtension
- : public ExtensionPrefsPreferencesBase {
+class ExtensionPrefsUninstallExtension : public ExtensionPrefsPrepopulatedTest {
virtual void Initialize() {
InstallExtControlledPref(ext1_, kPref1, Value::CreateStringValue("val1"));
InstallExtControlledPref(ext1_, kPref2, Value::CreateStringValue("val2"));
@@ -1060,11 +913,10 @@ class ExtensionPrefsUninstallExtension
}
};
TEST_F(ExtensionPrefsUninstallExtension,
- ExtensionPrefsUninstallExtension) {}
+ ExtensionPrefsUninstallExtension) {}
// Tests triggering of notifications to registered observers.
-class ExtensionPrefsNotifyWhenNeeded
- : public ExtensionPrefsPreferencesBase {
+class ExtensionPrefsNotifyWhenNeeded : public ExtensionPrefsPrepopulatedTest {
virtual void Initialize() {
using testing::_;
using testing::Mock;
@@ -1140,8 +992,7 @@ TEST_F(ExtensionPrefsNotifyWhenNeeded,
ExtensionPrefsNotifyWhenNeeded) {}
// Tests disabling an extension.
-class ExtensionPrefsDisableExt
- : public ExtensionPrefsPreferencesBase {
+class ExtensionPrefsDisableExt : public ExtensionPrefsPrepopulatedTest {
virtual void Initialize() {
InstallExtControlledPref(ext1_, kPref1, Value::CreateStringValue("val1"));
std::string actual = prefs()->pref_service()->GetString(kPref1);
@@ -1156,8 +1007,7 @@ class ExtensionPrefsDisableExt
TEST_F(ExtensionPrefsDisableExt, ExtensionPrefsDisableExt) {}
// Tests disabling and reenabling an extension.
-class ExtensionPrefsReenableExt
- : public ExtensionPrefsPreferencesBase {
+class ExtensionPrefsReenableExt : public ExtensionPrefsPrepopulatedTest {
virtual void Initialize() {
InstallExtControlledPref(ext1_, kPref1, Value::CreateStringValue("val1"));
prefs()->SetExtensionState(ext1_->id(), Extension::DISABLED);
@@ -1183,7 +1033,7 @@ class MockStringValue : public StringValue {
};
class ExtensionPrefsSetExtensionControlledPref
- : public ExtensionPrefsPreferencesBase {
+ : public ExtensionPrefsPrepopulatedTest {
public:
virtual void Initialize() {
MockStringValue* v1 = new MockStringValue("https://www.chromium.org");
@@ -1219,8 +1069,7 @@ TEST_F(ExtensionPrefsSetExtensionControlledPref,
// Tests that the switches::kDisableExtensions command-line flag prevents
// extension controlled preferences from being enacted.
-class ExtensionPrefsDisableExtensions
- : public ExtensionPrefsPreferencesBase {
+class ExtensionPrefsDisableExtensions : public ExtensionPrefsPrepopulatedTest {
public:
ExtensionPrefsDisableExtensions()
: iteration_(0) {}
@@ -1244,162 +1093,3 @@ class ExtensionPrefsDisableExtensions
int iteration_;
};
TEST_F(ExtensionPrefsDisableExtensions, ExtensionPrefsDisableExtensions) {}
-
-// Tests the application index to ordinal migration code. This should be removed
-// when the migrate code is taken out.
-class ExtensionPrefsMigrateAppIndex : public ExtensionPrefsPreferencesBase {
- public:
- ExtensionPrefsMigrateAppIndex() {}
- virtual ~ExtensionPrefsMigrateAppIndex() {}
- virtual void Initialize() {
- // A preference determining the order of which the apps appear on the NTP.
- const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
- // A preference determining the page on which an app appears in the NTP.
- const char kPrefPageIndexDeprecated[] = "page_index";
-
- // Setup the deprecated preferences.
- prefs()->UpdateExtensionPref(ext1_->id(),
- kPrefAppLaunchIndexDeprecated,
- Value::CreateIntegerValue(0));
- prefs()->UpdateExtensionPref(ext1_->id(),
- kPrefPageIndexDeprecated,
- Value::CreateIntegerValue(0));
-
- prefs()->UpdateExtensionPref(ext2_->id(),
- kPrefAppLaunchIndexDeprecated,
- Value::CreateIntegerValue(1));
- prefs()->UpdateExtensionPref(ext2_->id(),
- kPrefPageIndexDeprecated,
- Value::CreateIntegerValue(0));
-
- prefs()->UpdateExtensionPref(ext3_->id(),
- kPrefAppLaunchIndexDeprecated,
- Value::CreateIntegerValue(0));
- prefs()->UpdateExtensionPref(ext3_->id(),
- kPrefPageIndexDeprecated,
- Value::CreateIntegerValue(1));
-
- // We insert the ids in reserve order so that we have to deal with the
- // element on the 2nd page before the 1st page is seen.
- ExtensionPrefs::ExtensionIdSet ids;
- ids.push_back(ext3_->id());
- ids.push_back(ext2_->id());
- ids.push_back(ext1_->id());
-
- prefs_.prefs()->MigrateAppIndex(ids);
- }
- virtual void Verify() {
- StringOrdinal first_ordinal = StringOrdinal::CreateInitialOrdinal();
-
- EXPECT_TRUE(first_ordinal.Equal(prefs()->GetAppLaunchOrdinal(ext1_->id())));
- EXPECT_TRUE(first_ordinal.LessThan(
- prefs()->GetAppLaunchOrdinal(ext2_->id())));
- EXPECT_TRUE(first_ordinal.Equal(prefs()->GetAppLaunchOrdinal(ext3_->id())));
-
- EXPECT_TRUE(first_ordinal.Equal(prefs()->GetPageOrdinal(ext1_->id())));
- EXPECT_TRUE(first_ordinal.Equal(prefs()->GetPageOrdinal(ext2_->id())));
- EXPECT_TRUE(first_ordinal.LessThan(prefs()->GetPageOrdinal(ext3_->id())));
- }
-};
-TEST_F(ExtensionPrefsMigrateAppIndex, ExtensionPrefsMigrateAppIndex) {}
-
-// Tests the application index to ordinal migration code for values that
-// shouldn't be converted. This should be removed when the migrate code
-// is taken out.
-// http://crbug.com/107376
-class ExtensionPrefsMigrateAppIndexInvalid
- : public ExtensionPrefsPreferencesBase {
- public:
- ExtensionPrefsMigrateAppIndexInvalid() {}
- virtual ~ExtensionPrefsMigrateAppIndexInvalid() {}
- virtual void Initialize() {
- // A preference determining the order of which the apps appear on the NTP.
- const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
- // A preference determining the page on which an app appears in the NTP.
- const char kPrefPageIndexDeprecated[] = "page_index";
-
- // Setup the deprecated preference.
- prefs()->UpdateExtensionPref(ext1_->id(),
- kPrefAppLaunchIndexDeprecated,
- Value::CreateIntegerValue(0));
- prefs()->UpdateExtensionPref(ext1_->id(),
- kPrefPageIndexDeprecated,
- Value::CreateIntegerValue(-1));
-
- ExtensionPrefs::ExtensionIdSet ids;
- ids.push_back(ext1_->id());
-
- prefs_.prefs()->MigrateAppIndex(ids);
- }
- virtual void Verify() {
- // Make sure that the invalid page_index wasn't converted over.
- EXPECT_FALSE(prefs()->GetAppLaunchOrdinal(ext1_->id()).IsValid());
- }
-};
-TEST_F(ExtensionPrefsMigrateAppIndexInvalid,
- ExtensionPrefsMigrateAppIndexInvalid) {}
-
-class ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage :
- public ExtensionPrefsPreferencesBase {
- public:
- ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage() {}
- virtual ~ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage() {}
- virtual void Initialize() {
- DictionaryValue simple_dict;
- simple_dict.SetString(keys::kVersion, "1.0.0.0");
- simple_dict.SetString(keys::kName, "unused");
- simple_dict.SetString(keys::kApp, "true");
- simple_dict.SetString(keys::kLaunchLocalPath, "fake.html");
-
- std::string error;
- app1_scoped_ = Extension::Create(
- prefs_.temp_dir().AppendASCII("app1_"), Extension::EXTERNAL_PREF,
- simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
- prefs()->OnExtensionInstalled(app1_scoped_.get(),
- Extension::ENABLED,
- false,
- StringOrdinal());
-
- app2_scoped_ = Extension::Create(
- prefs_.temp_dir().AppendASCII("app2_"), Extension::EXTERNAL_PREF,
- simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
- prefs()->OnExtensionInstalled(app2_scoped_.get(),
- Extension::ENABLED,
- false,
- StringOrdinal());
- }
- virtual void Verify() {
- StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
-
- StringOrdinal min = prefs()->GetMinOrMaxAppLaunchOrdinalsOnPage(
- page,
- ExtensionPrefs::MIN_ORDINAL);
- StringOrdinal max = prefs()->GetMinOrMaxAppLaunchOrdinalsOnPage(
- page,
- ExtensionPrefs::MAX_ORDINAL);
- EXPECT_TRUE(min.IsValid());
- EXPECT_TRUE(max.IsValid());
- EXPECT_TRUE(min.LessThan(max));
-
- // Ensure that the min and max values aren't set for empty pages.
- min = StringOrdinal();
- max = StringOrdinal();
- StringOrdinal empty_page = page.CreateAfter();
- EXPECT_FALSE(min.IsValid());
- EXPECT_FALSE(max.IsValid());
- min = prefs()->GetMinOrMaxAppLaunchOrdinalsOnPage(
- empty_page,
- ExtensionPrefs::MIN_ORDINAL);
- max = prefs()->GetMinOrMaxAppLaunchOrdinalsOnPage(
- empty_page,
- ExtensionPrefs::MAX_ORDINAL);
- EXPECT_FALSE(min.IsValid());
- EXPECT_FALSE(max.IsValid());
- }
-
- private:
- scoped_refptr<Extension> app1_scoped_;
- scoped_refptr<Extension> app2_scoped_;
-};
-TEST_F(ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage,
- ExtensionPrefsGetMinOrMaxAppLaunchOrdinalsOnPage) {}
diff --git a/chrome/browser/extensions/extension_prefs_unittest.h b/chrome/browser/extensions/extension_prefs_unittest.h
new file mode 100644
index 0000000..18040e4
--- /dev/null
+++ b/chrome/browser/extensions/extension_prefs_unittest.h
@@ -0,0 +1,92 @@
+// 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_PREFS_UNITTEST_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_UNITTEST_H_
+#pragma once
+
+#include "base/message_loop.h"
+#include "chrome/browser/extensions/test_extension_prefs.h"
+#include "content/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Base class for extension preference-related unit tests.
+class ExtensionPrefsTest : public testing::Test {
+ public:
+ ExtensionPrefsTest();
+ virtual ~ExtensionPrefsTest();
+
+ // This function will get called once, and is the right place to do operations
+ // on ExtensionPrefs that write data.
+ virtual void Initialize() = 0;
+
+ // This function will be called twice - once while the original ExtensionPrefs
+ // object is still alive, and once after recreation. Thus, it tests that
+ // things don't break after any ExtensionPrefs startup work.
+ virtual void Verify() = 0;
+
+ // This function is called to Register preference default values.
+ virtual void RegisterPreferences();
+
+ virtual void SetUp() OVERRIDE;
+
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ ExtensionPrefs* prefs() { return prefs_.prefs(); }
+
+ MessageLoop message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+
+ TestExtensionPrefs prefs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPrefsTest);
+};
+
+class ExtensionPrefsPrepopulatedTest : public ExtensionPrefsTest {
+ public:
+ ExtensionPrefsPrepopulatedTest();
+ virtual ~ExtensionPrefsPrepopulatedTest();
+
+ virtual void RegisterPreferences() OVERRIDE;
+
+ void InstallExtControlledPref(Extension *ext,
+ const std::string& key,
+ Value* val);
+
+ void InstallExtControlledPrefIncognito(Extension *ext,
+ const std::string& key,
+ Value* val);
+
+ void InstallExtControlledPrefIncognitoSessionOnly(
+ Extension *ext,
+ const std::string& key,
+ Value* val);
+
+ void InstallExtension(Extension *ext);
+
+ void UninstallExtension(const std::string& extension_id);
+
+ // Weak references, for convenience.
+ Extension* ext1_;
+ Extension* ext2_;
+ Extension* ext3_;
+
+ // Flags indicating whether each of the extensions has been installed, yet.
+ bool installed[3];
+
+ private:
+ void EnsureExtensionInstalled(Extension *ext);
+
+ void EnsureExtensionUninstalled(const std::string& extension_id);
+
+ scoped_refptr<Extension> ext1_scoped_;
+ scoped_refptr<Extension> ext2_scoped_;
+ scoped_refptr<Extension> ext3_scoped_;
+};
+
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PREFS_UNITTEST_H_
diff --git a/chrome/browser/extensions/extension_scoped_prefs.h b/chrome/browser/extensions/extension_scoped_prefs.h
new file mode 100644
index 0000000..ba4f96a9
--- /dev/null
+++ b/chrome/browser/extensions/extension_scoped_prefs.h
@@ -0,0 +1,43 @@
+// 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_SCOPED_PREFS_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SCOPED_PREFS_H_
+#pragma once
+
+class ExtensionScopedPrefs {
+ public:
+ ExtensionScopedPrefs() {}
+ ~ExtensionScopedPrefs() {}
+
+ // Sets the pref |key| for extension |id| to |value|.
+ virtual void UpdateExtensionPref(const std::string& id,
+ const std::string& key,
+ base::Value* value) = 0;
+
+ // Deletes the pref dictionary for extension |id|.
+ virtual void DeleteExtensionPrefs(const std::string& id) = 0;
+
+ // Reads a boolean pref |pref_key| from extension with id |extension_id|.
+ virtual bool ReadExtensionPrefBoolean(const std::string& extension_id,
+ const std::string& pref_key) const = 0;
+
+ // Reads an integer pref |pref_key| from extension with id |extension_id|.
+ virtual bool ReadExtensionPrefInteger(const std::string& extension_id,
+ const std::string& pref_key,
+ int* out_value) const = 0;
+
+ // Reads a list pref |pref_key| from extension with id |extension_id|.
+ virtual bool ReadExtensionPrefList(
+ const std::string& extension_id,
+ const std::string& pref_key,
+ const base::ListValue** out_value) const = 0;
+
+ // Reads a string pref |pref_key| from extension with id |extension_id|.
+ virtual bool ReadExtensionPrefString(const std::string& extension_id,
+ const std::string& pref_key,
+ std::string* out_value) const = 0;
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SCOPED_PREFS_H_
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index cadf4ff..f8dd4cc 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -48,6 +48,7 @@
#include "chrome/browser/extensions/extension_preference_api.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_processes_api.h"
+#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/extensions/extension_updater.h"
@@ -1609,7 +1610,8 @@ bool ExtensionService::CanLoadInIncognito(const Extension* extension) const {
StringOrdinal ExtensionService::GetAppLaunchOrdinal(
const std::string& extension_id) const {
- return extension_prefs_->GetAppLaunchOrdinal(extension_id);
+ return
+ extension_prefs_->extension_sorting()->GetAppLaunchOrdinal(extension_id);
}
void ExtensionService::SetAppLaunchOrdinal(
@@ -1618,7 +1620,8 @@ void ExtensionService::SetAppLaunchOrdinal(
const Extension* ext = GetExtensionById(extension_id, true);
DCHECK(ext->is_app());
- extension_prefs_->SetAppLaunchOrdinal(extension_id, app_launch_index);
+ extension_prefs_->extension_sorting()->SetAppLaunchOrdinal(
+ extension_id, app_launch_index);
const Extension* extension = GetInstalledExtension(extension_id);
if (extension)
@@ -1627,7 +1630,7 @@ void ExtensionService::SetAppLaunchOrdinal(
StringOrdinal ExtensionService::GetPageOrdinal(
const std::string& extension_id) const {
- return extension_prefs_->GetPageOrdinal(extension_id);
+ return extension_prefs_->extension_sorting()->GetPageOrdinal(extension_id);
}
void ExtensionService::SetPageOrdinal(const std::string& extension_id,
@@ -1635,7 +1638,8 @@ void ExtensionService::SetPageOrdinal(const std::string& extension_id,
const Extension* ext = GetExtensionById(extension_id, true);
DCHECK(ext->is_app());
- extension_prefs_->SetPageOrdinal(extension_id, page_ordinal);
+ extension_prefs_->extension_sorting()->SetPageOrdinal(
+ extension_id, page_ordinal);
const Extension* extension = GetInstalledExtension(extension_id);
if (extension)
@@ -1646,9 +1650,10 @@ void ExtensionService::OnExtensionMoved(
const std::string& moved_extension_id,
const std::string& predecessor_extension_id,
const std::string& successor_extension_id) {
- extension_prefs_->OnExtensionMoved(moved_extension_id,
- predecessor_extension_id,
- successor_extension_id);
+ extension_prefs_->extension_sorting()->OnExtensionMoved(
+ moved_extension_id,
+ predecessor_extension_id,
+ successor_extension_id);
const Extension* extension = GetInstalledExtension(moved_extension_id);
if (extension)
diff --git a/chrome/browser/extensions/extension_sorting.cc b/chrome/browser/extensions/extension_sorting.cc
new file mode 100644
index 0000000..30914d6
--- /dev/null
+++ b/chrome/browser/extensions/extension_sorting.cc
@@ -0,0 +1,364 @@
+// 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_sorting.h"
+
+#include "chrome/browser/extensions/extension_scoped_prefs.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/notification_service.h"
+
+namespace {
+
+// The number of apps per page. This isn't a hard limit, but new apps installed
+// from the webstore will overflow onto a new page if this limit is reached.
+const int kNaturalAppPageSize = 18;
+
+// A preference determining the order of which the apps appear on the NTP.
+const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
+const char kPrefAppLaunchOrdinal[] = "app_launcher_ordinal";
+
+// A preference determining the page on which an app appears in the NTP.
+const char kPrefPageIndexDeprecated[] = "page_index";
+const char kPrefPageOrdinal[] = "page_ordinal";
+
+} // namespace
+
+ExtensionSorting::ExtensionSorting(ExtensionScopedPrefs* extension_scoped_prefs,
+ PrefService* pref_service)
+ : extension_scoped_prefs_(extension_scoped_prefs),
+ pref_service_(pref_service) {
+}
+
+ExtensionSorting::~ExtensionSorting() {
+}
+
+void ExtensionSorting::MigrateAppIndex(
+ const ExtensionPrefs::ExtensionIdSet& extension_ids) {
+ if (extension_ids.empty())
+ return;
+
+ InitializePageOrdinalMap(extension_ids);
+
+ // Convert all the page index values to page ordinals. If there are any
+ // app launch values that need to be migrated, inserted them into a sorted
+ // set to be dealt with later.
+ typedef std::map<StringOrdinal, std::map<int, const std::string*>,
+ StringOrdinalLessThan> AppPositionToIdMapping;
+ AppPositionToIdMapping app_launches_to_convert;
+ for (ExtensionPrefs::ExtensionIdSet::const_iterator ext_id =
+ extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) {
+ int old_page_index = 0;
+ StringOrdinal page = GetPageOrdinal(*ext_id);
+ if (extension_scoped_prefs_->ReadExtensionPrefInteger(
+ *ext_id,
+ kPrefPageIndexDeprecated,
+ &old_page_index)) {
+ // Some extensions have invalid page index, so we don't
+ // attempt to convert them.
+ if (old_page_index < 0) {
+ DLOG(WARNING) << "Extension " << *ext_id
+ << " has an invalid page index " << old_page_index
+ << ". Aborting attempt to convert its index.";
+ break;
+ }
+
+ // Since we require all earlier StringOrdinals to already exist in order
+ // to properly convert from integers and we are iterating though them in
+ // no given order, we create earlier StringOrdinal values as required.
+ // This should be filled in by the time we are done with this loop.
+ if (page_ordinal_map_.empty())
+ page_ordinal_map_[StringOrdinal::CreateInitialOrdinal()] = 0;
+ while (page_ordinal_map_.size() <= static_cast<size_t>(old_page_index)) {
+ StringOrdinal earlier_page =
+ page_ordinal_map_.rbegin()->first.CreateAfter();
+ page_ordinal_map_[earlier_page] = 0;
+ }
+
+ page = PageIntegerAsStringOrdinal(old_page_index);
+ SetPageOrdinal(*ext_id, page);
+ extension_scoped_prefs_->UpdateExtensionPref(
+ *ext_id, kPrefPageIndexDeprecated, NULL);
+ }
+
+ int old_app_launch_index = 0;
+ if (extension_scoped_prefs_->ReadExtensionPrefInteger(
+ *ext_id,
+ kPrefAppLaunchIndexDeprecated,
+ &old_app_launch_index)) {
+ // We can't update the app launch index value yet, because we use
+ // GetNextAppLaunchOrdinal to get the new ordinal value and it requires
+ // all the ordinals with lower values to have already been migrated.
+ // A valid page ordinal is also required because otherwise there is
+ // no page to add the app to.
+ if (page.IsValid())
+ app_launches_to_convert[page][old_app_launch_index] = &*ext_id;
+
+ extension_scoped_prefs_->UpdateExtensionPref(
+ *ext_id, kPrefAppLaunchIndexDeprecated, NULL);
+ }
+ }
+
+ // Remove any empty pages that may have been added. This shouldn't occur,
+ // but double check here to prevent future problems with conversions between
+ // integers and StringOrdinals.
+ for (PageOrdinalMap::iterator it = page_ordinal_map_.begin();
+ it != page_ordinal_map_.end();) {
+ if (it->second == 0) {
+ PageOrdinalMap::iterator prev_it = it;
+ ++it;
+ page_ordinal_map_.erase(prev_it);
+ } else {
+ ++it;
+ }
+ }
+
+ if (app_launches_to_convert.empty())
+ return;
+
+ // Create the new app launch ordinals and remove the old preferences. Since
+ // the set is sorted, each time we migrate an apps index, we know that all of
+ // the remaining apps will appear further down the NTP than it or on a
+ // different page.
+ for (AppPositionToIdMapping::const_iterator page_it =
+ app_launches_to_convert.begin();
+ page_it != app_launches_to_convert.end(); ++page_it) {
+ StringOrdinal page = page_it->first;
+ for (std::map<int, const std::string*>::const_iterator launch_it =
+ page_it->second.begin(); launch_it != page_it->second.end();
+ ++launch_it) {
+ SetAppLaunchOrdinal(*(launch_it->second),
+ CreateNextAppLaunchOrdinal(page));
+ }
+ }
+}
+
+void ExtensionSorting::OnExtensionMoved(
+ const std::string& moved_extension_id,
+ const std::string& predecessor_extension_id,
+ const std::string& successor_extension_id) {
+ // If there are no neighbors then no changes are required, so we can return.
+ if (predecessor_extension_id.empty() && successor_extension_id.empty())
+ return;
+
+ if (predecessor_extension_id.empty()) {
+ SetAppLaunchOrdinal(
+ moved_extension_id,
+ GetAppLaunchOrdinal(successor_extension_id).CreateBefore());
+ } else if (successor_extension_id.empty()) {
+ SetAppLaunchOrdinal(
+ moved_extension_id,
+ GetAppLaunchOrdinal(predecessor_extension_id).CreateAfter());
+ } else {
+ const StringOrdinal& predecessor_ordinal =
+ GetAppLaunchOrdinal(predecessor_extension_id);
+ const StringOrdinal& successor_ordinal =
+ GetAppLaunchOrdinal(successor_extension_id);
+ SetAppLaunchOrdinal(moved_extension_id,
+ predecessor_ordinal.CreateBetween(successor_ordinal));
+ }
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
+ content::Source<ExtensionSorting>(this),
+ content::NotificationService::NoDetails());
+}
+
+
+StringOrdinal ExtensionSorting::GetAppLaunchOrdinal(
+ const std::string& extension_id) const {
+ std::string raw_value;
+ // If the preference read fails then raw_value will still be unset and we
+ // will return an invalid StringOrdinal to signal that no app launch ordinal
+ // was found.
+ extension_scoped_prefs_->ReadExtensionPrefString(
+ extension_id, kPrefAppLaunchOrdinal, &raw_value);
+ return StringOrdinal(raw_value);
+}
+
+void ExtensionSorting::SetAppLaunchOrdinal(const std::string& extension_id,
+ const StringOrdinal& ordinal) {
+ extension_scoped_prefs_->UpdateExtensionPref(
+ extension_id,
+ kPrefAppLaunchOrdinal,
+ Value::CreateStringValue(ordinal.ToString()));
+}
+
+StringOrdinal ExtensionSorting::CreateFirstAppLaunchOrdinal(
+ const StringOrdinal& page_ordinal) const {
+ const StringOrdinal& min_ordinal =
+ GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
+ ExtensionSorting::MIN_ORDINAL);
+
+ if (min_ordinal.IsValid())
+ return min_ordinal.CreateBefore();
+ else
+ return StringOrdinal::CreateInitialOrdinal();
+}
+
+StringOrdinal ExtensionSorting::CreateNextAppLaunchOrdinal(
+ const StringOrdinal& page_ordinal) const {
+ const StringOrdinal& max_ordinal =
+ GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
+ ExtensionSorting::MAX_ORDINAL);
+
+ if (max_ordinal.IsValid())
+ return max_ordinal.CreateAfter();
+ else
+ return StringOrdinal::CreateInitialOrdinal();
+}
+
+StringOrdinal ExtensionSorting::CreateFirstAppPageOrdinal() const {
+ const DictionaryValue* extensions = pref_service_->GetDictionary(
+ ExtensionPrefs::kExtensionsPref);
+ CHECK(extensions);
+
+ if (page_ordinal_map_.empty())
+ return StringOrdinal::CreateInitialOrdinal();
+
+ return page_ordinal_map_.begin()->first;
+}
+
+StringOrdinal ExtensionSorting::GetNaturalAppPageOrdinal() const {
+ const DictionaryValue* extensions = pref_service_->GetDictionary(
+ ExtensionPrefs::kExtensionsPref);
+ CHECK(extensions);
+
+ if (page_ordinal_map_.empty())
+ return StringOrdinal::CreateInitialOrdinal();
+
+ for (PageOrdinalMap::const_iterator it = page_ordinal_map_.begin();
+ it != page_ordinal_map_.end(); ++it) {
+ if (it->second < kNaturalAppPageSize)
+ return it->first;
+ }
+
+ // Add a new page as all existing pages are full.
+ StringOrdinal last_element = page_ordinal_map_.rbegin()->first;
+ return last_element.CreateAfter();
+}
+
+StringOrdinal ExtensionSorting::GetPageOrdinal(const std::string& extension_id)
+ const {
+ 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 page ordinal was found.
+ extension_scoped_prefs_->ReadExtensionPrefString(
+ extension_id, kPrefPageOrdinal, &raw_data);
+ return StringOrdinal(raw_data);
+}
+
+void ExtensionSorting::SetPageOrdinal(const std::string& extension_id,
+ const StringOrdinal& page_ordinal) {
+ UpdatePageOrdinalMap(GetPageOrdinal(extension_id), page_ordinal);
+ extension_scoped_prefs_->UpdateExtensionPref(
+ extension_id,
+ kPrefPageOrdinal,
+ Value::CreateStringValue(page_ordinal.ToString()));
+}
+
+void ExtensionSorting::ClearPageOrdinal(const std::string& extension_id) {
+ UpdatePageOrdinalMap(GetPageOrdinal(extension_id), StringOrdinal());
+ extension_scoped_prefs_->UpdateExtensionPref(
+ extension_id, kPrefPageOrdinal, NULL);
+}
+
+int ExtensionSorting::PageStringOrdinalAsInteger(
+ const StringOrdinal& page_ordinal) const {
+ if (!page_ordinal.IsValid())
+ return -1;
+
+ PageOrdinalMap::const_iterator it = page_ordinal_map_.find(page_ordinal);
+ if (it != page_ordinal_map_.end()) {
+ return std::distance(page_ordinal_map_.begin(), it);
+ } else {
+ return -1;
+ }
+}
+
+StringOrdinal ExtensionSorting::PageIntegerAsStringOrdinal(size_t page_index)
+ const {
+ // We shouldn't have a page_index that is more than 1 position away from the
+ // current end as that would imply we have empty pages which is not allowed.
+ CHECK_LE(page_index, page_ordinal_map_.size());
+
+ const DictionaryValue* extensions = pref_service_->GetDictionary(
+ ExtensionPrefs::kExtensionsPref);
+ if (!extensions)
+ return StringOrdinal();
+
+ if (page_index < page_ordinal_map_.size()) {
+ PageOrdinalMap::const_iterator it = page_ordinal_map_.begin();
+ std::advance(it, page_index);
+
+ return it->first;
+
+ } else {
+ if (page_ordinal_map_.empty())
+ return StringOrdinal::CreateInitialOrdinal();
+ else
+ return page_ordinal_map_.rbegin()->first.CreateAfter();
+ }
+}
+
+StringOrdinal ExtensionSorting::GetMinOrMaxAppLaunchOrdinalsOnPage(
+ const StringOrdinal& target_page_ordinal,
+ AppLaunchOrdinalReturn return_type) const {
+ const DictionaryValue* extensions = pref_service_->GetDictionary(
+ ExtensionPrefs::kExtensionsPref);
+ CHECK(extensions);
+ CHECK(target_page_ordinal.IsValid());
+
+ StringOrdinal return_value;
+ for (DictionaryValue::key_iterator ext_it = extensions->begin_keys();
+ ext_it != extensions->end_keys(); ++ext_it) {
+ const StringOrdinal& page_ordinal = GetPageOrdinal(*ext_it);
+ if (page_ordinal.IsValid() && page_ordinal.Equal(target_page_ordinal)) {
+ const StringOrdinal& app_launch_ordinal = GetAppLaunchOrdinal(*ext_it);
+ if (app_launch_ordinal.IsValid()) {
+ if (!return_value.IsValid())
+ return_value = app_launch_ordinal;
+ else if (return_type == ExtensionSorting::MIN_ORDINAL &&
+ app_launch_ordinal.LessThan(return_value))
+ return_value = app_launch_ordinal;
+ else if (return_type == ExtensionSorting::MAX_ORDINAL &&
+ app_launch_ordinal.GreaterThan(return_value))
+ return_value = app_launch_ordinal;
+ }
+ }
+ }
+
+ return return_value;
+}
+
+void ExtensionSorting::InitializePageOrdinalMap(
+ const ExtensionPrefs::ExtensionIdSet& extension_ids) {
+ for (ExtensionPrefs::ExtensionIdSet::const_iterator ext_it =
+ extension_ids.begin(); ext_it != extension_ids.end(); ++ext_it) {
+ UpdatePageOrdinalMap(StringOrdinal(), GetPageOrdinal(*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);
+ }
+
+ // Include the Web Store App since it is displayed on the NTP.
+ StringOrdinal web_store_app_page =
+ GetPageOrdinal(extension_misc::kWebStoreAppId);
+ if (web_store_app_page.IsValid())
+ UpdatePageOrdinalMap(StringOrdinal(), web_store_app_page);
+}
+
+void ExtensionSorting::UpdatePageOrdinalMap(const StringOrdinal& old_value,
+ const StringOrdinal& new_value) {
+ if (new_value.IsValid())
+ ++page_ordinal_map_[new_value];
+
+ if (old_value.IsValid()) {
+ --page_ordinal_map_[old_value];
+
+ if (page_ordinal_map_[old_value] == 0)
+ page_ordinal_map_.erase(old_value);
+ }
+}
diff --git a/chrome/browser/extensions/extension_sorting.h b/chrome/browser/extensions/extension_sorting.h
new file mode 100644
index 0000000..0a7e397
--- /dev/null
+++ b/chrome/browser/extensions/extension_sorting.h
@@ -0,0 +1,127 @@
+// 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_SORTING_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SORTING_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/common/string_ordinal.h"
+
+class ExtensionScopedPrefs;
+
+class ExtensionSorting {
+ public:
+ explicit ExtensionSorting(ExtensionScopedPrefs* extension_scoped_prefs,
+ PrefService* pref_service);
+ ~ExtensionSorting();
+
+ // Migrates the app launcher and page index values.
+ void MigrateAppIndex(const ExtensionPrefs::ExtensionIdSet& extension_ids);
+
+ // Updates the app launcher value for the moved extension so that it is now
+ // located after the given predecessor and before the successor.
+ // Empty strings are used to indicate no successor or predecessor.
+ void OnExtensionMoved(const std::string& moved_extension_id,
+ const std::string& predecessor_extension_id,
+ const std::string& successor_extension_id);
+
+ // Get the application launch ordinal for an app with |extension_id|. This
+ // determines the order in which the app appears on the page it's on in the
+ // New Tab Page (Note that you can compare app launch ordinals only if the
+ // apps are on the same page). A string value close to |a*| generally
+ // indicates top left. If the extension has no launch ordinal, an invalid
+ // StringOrdinal is returned.
+ StringOrdinal GetAppLaunchOrdinal(const std::string& extension_id) const;
+
+ // Sets a specific launch ordinal for an app with |extension_id|.
+ void SetAppLaunchOrdinal(const std::string& extension_id,
+ const StringOrdinal& ordinal);
+
+ // Returns a StringOrdinal that is lower than any app launch ordinal for the
+ // given page.
+ StringOrdinal CreateFirstAppLaunchOrdinal(const StringOrdinal& page_ordinal)
+ const;
+
+ // Returns a StringOrdinal that is higher than any app launch ordinal for the
+ // given page.
+ StringOrdinal CreateNextAppLaunchOrdinal(const StringOrdinal& page_ordinal)
+ const;
+
+ // Returns a StringOrdinal that is lower than any existing page ordinal.
+ StringOrdinal CreateFirstAppPageOrdinal() const;
+
+ // Gets the page a new app should install to, which is the earliest non-full
+ // page. The returned ordinal may correspond to a page that doesn't yet exist
+ // if all pages are full.
+ StringOrdinal GetNaturalAppPageOrdinal() const;
+
+ // Get the page ordinal for an app with |extension_id|. This determines
+ // which page an app will appear on in page-based NTPs. If the app has no
+ // page specified, an invalid StringOrdinal is returned.
+ StringOrdinal GetPageOrdinal(const std::string& extension_id) const;
+
+ // Sets a specific page ordinal for an app with |extension_id|.
+ void SetPageOrdinal(const std::string& extension_id,
+ const StringOrdinal& ordinal);
+
+ // Removes the page ordinal for an app.
+ void ClearPageOrdinal(const std::string& extension_id);
+
+ // Convert the page StringOrdinal value to its integer equivalent. This takes
+ // O(# of apps) worst-case.
+ int PageStringOrdinalAsInteger(const StringOrdinal& page_ordinal) const;
+
+ // Converts the page index integer to its StringOrdinal equivalent. This takes
+ // O(# of apps) worst-case.
+ StringOrdinal PageIntegerAsStringOrdinal(size_t page_index) const;
+
+ private:
+ friend class ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage;
+ // Unit test.
+
+ // An enum used by GetMinOrMaxAppLaunchOrdinalsOnPage to specify which
+ // value should be returned.
+ enum AppLaunchOrdinalReturn {MIN_ORDINAL, MAX_ORDINAL};
+
+ // This function returns the lowest ordinal on |page_ordinal| if
+ // |return_value| == AppLaunchOrdinalReturn::MIN_ORDINAL, otherwise it returns
+ // the largest ordinal on |page_ordinal|. If there are no apps on the page
+ // then an invalid StringOrdinal is returned. It is an error to call this
+ // function with an invalid |page_ordinal|.
+ StringOrdinal GetMinOrMaxAppLaunchOrdinalsOnPage(
+ const StringOrdinal& page_ordinal,
+ AppLaunchOrdinalReturn return_type) const;
+
+ // Initialize the |page_ordinal_map_| with the page ordinals used by the
+ // given extensions.
+ void InitializePageOrdinalMap(
+ const ExtensionPrefs::ExtensionIdSet& extension_ids);
+
+ // Called when an application changes the value of its page ordinal so that
+ // |page_ordinal_map_| is aware that |old_value| page ordinal has been
+ // replace by the |new_value| page ordinal and adjusts its mapping
+ // accordingly. This works with valid and invalid StringOrdinals.
+ void UpdatePageOrdinalMap(const StringOrdinal& old_value,
+ const StringOrdinal& new_value);
+
+ ExtensionScopedPrefs* extension_scoped_prefs_; // Weak, owns this instance.
+ PrefService* pref_service_; // Weak.
+
+ // A map of all the StringOrdinal page indices mapping to how often they are
+ // used, this is used for mapping the StringOrdinals to their integer
+ // equivalent as well as quick lookup of the sorted StringOrdinals.
+ // TODO(csharp) Convert this to a two-layer map to allow page ordinal lookup
+ // by id and vice versa.
+ typedef std::map<StringOrdinal, int, StringOrdinalLessThan> PageOrdinalMap;
+ PageOrdinalMap page_ordinal_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionSorting);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SORTING_H_
diff --git a/chrome/browser/extensions/extension_sorting_unittest.cc b/chrome/browser/extensions/extension_sorting_unittest.cc
new file mode 100644
index 0000000..cd86dae
--- /dev/null
+++ b/chrome/browser/extensions/extension_sorting_unittest.cc
@@ -0,0 +1,306 @@
+// 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_prefs_unittest.h"
+#include "chrome/browser/extensions/extension_sorting.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace keys = extension_manifest_keys;
+
+class ExtensionSortingTest : public ExtensionPrefsTest {
+ protected:
+ ExtensionSorting* extension_sorting() {
+ return prefs()->extension_sorting();
+ }
+};
+
+class ExtensionSortingAppLocation : public ExtensionSortingTest {
+ public:
+ virtual void Initialize() OVERRIDE {
+ extension_ = prefs_.AddExtension("not_an_app");
+ // Non-apps should not have any app launch ordinal or page ordinal.
+ prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+ false, StringOrdinal());
+ }
+
+ virtual void Verify() OVERRIDE {
+ EXPECT_FALSE(
+ extension_sorting()->GetAppLaunchOrdinal(extension_->id()).IsValid());
+ EXPECT_FALSE(
+ extension_sorting()->GetPageOrdinal(extension_->id()).IsValid());
+ }
+
+ private:
+ scoped_refptr<Extension> extension_;
+};
+TEST_F(ExtensionSortingAppLocation, ExtensionSortingAppLocation) {}
+
+class ExtensionSortingAppLaunchOrdinal : public ExtensionSortingTest {
+ public:
+ virtual void Initialize() OVERRIDE {
+ // No extensions yet.
+ StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
+ EXPECT_TRUE(StringOrdinal::CreateInitialOrdinal().Equal(
+ extension_sorting()->CreateNextAppLaunchOrdinal(page)));
+
+ extension_ = prefs_.AddApp("on_extension_installed");
+ EXPECT_FALSE(prefs()->IsExtensionDisabled(extension_->id()));
+ prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+ false, StringOrdinal());
+ }
+
+ virtual void Verify() OVERRIDE {
+ StringOrdinal launch_ordinal =
+ extension_sorting()->GetAppLaunchOrdinal(extension_->id());
+ StringOrdinal page_ordinal = StringOrdinal::CreateInitialOrdinal();
+
+ // Extension should have been assigned a valid StringOrdinal.
+ EXPECT_TRUE(launch_ordinal.IsValid());
+ EXPECT_TRUE(launch_ordinal.LessThan(
+ extension_sorting()->CreateNextAppLaunchOrdinal(page_ordinal)));
+ // Set a new launch ordinal of and verify it comes after.
+ extension_sorting()->SetAppLaunchOrdinal(
+ extension_->id(),
+ extension_sorting()->CreateNextAppLaunchOrdinal(page_ordinal));
+ StringOrdinal new_launch_ordinal =
+ extension_sorting()->GetAppLaunchOrdinal(extension_->id());
+ EXPECT_TRUE(launch_ordinal.LessThan(new_launch_ordinal));
+
+ // This extension doesn't exist, so it should return an invalid
+ // StringOrdinal.
+ StringOrdinal invalid_app_launch_ordinal =
+ extension_sorting()->GetAppLaunchOrdinal("foo");
+ EXPECT_FALSE(invalid_app_launch_ordinal.IsValid());
+ EXPECT_EQ(-1, extension_sorting()->PageStringOrdinalAsInteger(
+ invalid_app_launch_ordinal));
+
+ // The second page doesn't have any apps so its next launch ordinal should
+ // be the first launch ordinal.
+ StringOrdinal next_page = page_ordinal.CreateAfter();
+ StringOrdinal next_page_app_launch_ordinal =
+ extension_sorting()->CreateNextAppLaunchOrdinal(next_page);
+ EXPECT_TRUE(next_page_app_launch_ordinal.Equal(
+ extension_sorting()->CreateFirstAppLaunchOrdinal(next_page)));
+ }
+
+ private:
+ scoped_refptr<Extension> extension_;
+};
+TEST_F(ExtensionSortingAppLaunchOrdinal, ExtensionSortingAppLaunchOrdinal) {}
+
+class ExtensionSortingPageOrdinal : public ExtensionSortingTest {
+ public:
+ virtual void Initialize() OVERRIDE{
+ extension_ = prefs_.AddApp("page_ordinal");
+ // Install with a page preference.
+ StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
+ prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+ false, page);
+ EXPECT_TRUE(
+ page.Equal(extension_sorting()->GetPageOrdinal(extension_->id())));
+ EXPECT_EQ(0, extension_sorting()->PageStringOrdinalAsInteger(page));
+
+ scoped_refptr<Extension> extension2 = prefs_.AddApp("page_ordinal_2");
+ // Install without any page preference.
+ prefs()->OnExtensionInstalled(extension_.get(), Extension::ENABLED,
+ false, StringOrdinal());
+ EXPECT_TRUE(
+ extension_sorting()->GetPageOrdinal(extension_->id()).IsValid());
+ }
+
+ virtual void Verify() OVERRIDE {
+ StringOrdinal old_page =
+ extension_sorting()->GetPageOrdinal(extension_->id());
+ StringOrdinal new_page = old_page.CreateAfter();
+
+ // Set the page ordinal.
+ extension_sorting()->SetPageOrdinal(extension_->id(), new_page);
+ // Verify the page ordinal.
+ EXPECT_TRUE(
+ new_page.Equal(extension_sorting()->GetPageOrdinal(extension_->id())));
+ EXPECT_EQ(1, extension_sorting()->PageStringOrdinalAsInteger(new_page));
+
+ // This extension doesn't exist, so it should return an invalid
+ // StringOrdinal.
+ EXPECT_FALSE(extension_sorting()->GetPageOrdinal("foo").IsValid());
+ }
+
+ private:
+ scoped_refptr<Extension> extension_;
+};
+TEST_F(ExtensionSortingPageOrdinal, ExtensionSortingPageOrdinal) {}
+
+// Tests the application index to ordinal migration code. This should be removed
+// when the migrate code is taken out.
+class ExtensionSortingMigrateAppIndex
+ : public ExtensionPrefsPrepopulatedTest {
+ public:
+ ExtensionSortingMigrateAppIndex() {}
+ virtual ~ExtensionSortingMigrateAppIndex() {}
+ virtual void Initialize() OVERRIDE {
+ // A preference determining the order of which the apps appear on the NTP.
+ const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
+ // A preference determining the page on which an app appears in the NTP.
+ const char kPrefPageIndexDeprecated[] = "page_index";
+
+ // Setup the deprecated preferences.
+ ExtensionScopedPrefs* scoped_prefs =
+ static_cast<ExtensionScopedPrefs*>(prefs());
+ scoped_prefs->UpdateExtensionPref(ext1_->id(),
+ kPrefAppLaunchIndexDeprecated,
+ Value::CreateIntegerValue(0));
+ scoped_prefs->UpdateExtensionPref(ext1_->id(),
+ kPrefPageIndexDeprecated,
+ Value::CreateIntegerValue(0));
+
+ scoped_prefs->UpdateExtensionPref(ext2_->id(),
+ kPrefAppLaunchIndexDeprecated,
+ Value::CreateIntegerValue(1));
+ scoped_prefs->UpdateExtensionPref(ext2_->id(),
+ kPrefPageIndexDeprecated,
+ Value::CreateIntegerValue(0));
+
+ scoped_prefs->UpdateExtensionPref(ext3_->id(),
+ kPrefAppLaunchIndexDeprecated,
+ Value::CreateIntegerValue(0));
+ scoped_prefs->UpdateExtensionPref(ext3_->id(),
+ kPrefPageIndexDeprecated,
+ Value::CreateIntegerValue(1));
+
+ // We insert the ids in reserve order so that we have to deal with the
+ // element on the 2nd page before the 1st page is seen.
+ ExtensionPrefs::ExtensionIdSet ids;
+ ids.push_back(ext3_->id());
+ ids.push_back(ext2_->id());
+ ids.push_back(ext1_->id());
+
+ prefs()->extension_sorting()->MigrateAppIndex(ids);
+ }
+ virtual void Verify() OVERRIDE {
+ StringOrdinal first_ordinal = StringOrdinal::CreateInitialOrdinal();
+ ExtensionSorting* extension_sorting = prefs()->extension_sorting();
+
+ EXPECT_TRUE(first_ordinal.Equal(
+ extension_sorting->GetAppLaunchOrdinal(ext1_->id())));
+ EXPECT_TRUE(first_ordinal.LessThan(
+ extension_sorting->GetAppLaunchOrdinal(ext2_->id())));
+ EXPECT_TRUE(first_ordinal.Equal(
+ extension_sorting->GetAppLaunchOrdinal(ext3_->id())));
+
+ EXPECT_TRUE(first_ordinal.Equal(
+ extension_sorting->GetPageOrdinal(ext1_->id())));
+ EXPECT_TRUE(first_ordinal.Equal(
+ extension_sorting->GetPageOrdinal(ext2_->id())));
+ EXPECT_TRUE(first_ordinal.LessThan(
+ extension_sorting->GetPageOrdinal(ext3_->id())));
+ }
+};
+TEST_F(ExtensionSortingMigrateAppIndex, ExtensionSortingMigrateAppIndex) {}
+
+// Tests the application index to ordinal migration code for values that
+// shouldn't be converted. This should be removed when the migrate code
+// is taken out.
+// http://crbug.com/107376
+class ExtensionSortingMigrateAppIndexInvalid
+ : public ExtensionPrefsPrepopulatedTest {
+ public:
+ ExtensionSortingMigrateAppIndexInvalid() {}
+ virtual ~ExtensionSortingMigrateAppIndexInvalid() {}
+ virtual void Initialize() OVERRIDE {
+ // A preference determining the order of which the apps appear on the NTP.
+ const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
+ // A preference determining the page on which an app appears in the NTP.
+ const char kPrefPageIndexDeprecated[] = "page_index";
+
+ // Setup the deprecated preference.
+ ExtensionScopedPrefs* scoped_prefs =
+ static_cast<ExtensionScopedPrefs*>(prefs());
+ scoped_prefs->UpdateExtensionPref(ext1_->id(),
+ kPrefAppLaunchIndexDeprecated,
+ Value::CreateIntegerValue(0));
+ scoped_prefs->UpdateExtensionPref(ext1_->id(),
+ kPrefPageIndexDeprecated,
+ Value::CreateIntegerValue(-1));
+
+ ExtensionPrefs::ExtensionIdSet ids;
+ ids.push_back(ext1_->id());
+
+ prefs()->extension_sorting()->MigrateAppIndex(ids);
+ }
+ virtual void Verify() OVERRIDE {
+ // Make sure that the invalid page_index wasn't converted over.
+ EXPECT_FALSE(prefs()->extension_sorting()->GetAppLaunchOrdinal(
+ ext1_->id()).IsValid());
+ }
+};
+TEST_F(ExtensionSortingMigrateAppIndexInvalid,
+ ExtensionSortingMigrateAppIndexInvalid) {}
+
+class ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage :
+ public ExtensionPrefsPrepopulatedTest {
+ public:
+ ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {}
+ virtual ~ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage() {}
+
+ virtual void Initialize() OVERRIDE {
+ DictionaryValue simple_dict;
+ simple_dict.SetString(keys::kVersion, "1.0.0.0");
+ simple_dict.SetString(keys::kName, "unused");
+ simple_dict.SetString(keys::kApp, "true");
+ simple_dict.SetString(keys::kLaunchLocalPath, "fake.html");
+
+ std::string error;
+ app1_scoped_ = Extension::Create(
+ prefs_.temp_dir().AppendASCII("app1_"), Extension::EXTERNAL_PREF,
+ simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
+ prefs()->OnExtensionInstalled(app1_scoped_.get(),
+ Extension::ENABLED,
+ false,
+ StringOrdinal());
+
+ app2_scoped_ = Extension::Create(
+ prefs_.temp_dir().AppendASCII("app2_"), Extension::EXTERNAL_PREF,
+ simple_dict, Extension::STRICT_ERROR_CHECKS, &error);
+ prefs()->OnExtensionInstalled(app2_scoped_.get(),
+ Extension::ENABLED,
+ false,
+ StringOrdinal());
+ }
+ virtual void Verify() OVERRIDE {
+ StringOrdinal page = StringOrdinal::CreateInitialOrdinal();
+ ExtensionSorting* extension_sorting = prefs()->extension_sorting();
+
+ StringOrdinal min = extension_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
+ page,
+ ExtensionSorting::MIN_ORDINAL);
+ StringOrdinal max = extension_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
+ page,
+ ExtensionSorting::MAX_ORDINAL);
+ EXPECT_TRUE(min.IsValid());
+ EXPECT_TRUE(max.IsValid());
+ EXPECT_TRUE(min.LessThan(max));
+
+ // Ensure that the min and max values aren't set for empty pages.
+ min = StringOrdinal();
+ max = StringOrdinal();
+ StringOrdinal empty_page = page.CreateAfter();
+ EXPECT_FALSE(min.IsValid());
+ EXPECT_FALSE(max.IsValid());
+ min = extension_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
+ empty_page,
+ ExtensionSorting::MIN_ORDINAL);
+ max = extension_sorting->GetMinOrMaxAppLaunchOrdinalsOnPage(
+ empty_page,
+ ExtensionSorting::MAX_ORDINAL);
+ EXPECT_FALSE(min.IsValid());
+ EXPECT_FALSE(max.IsValid());
+ }
+
+ private:
+ scoped_refptr<Extension> app1_scoped_;
+ scoped_refptr<Extension> app2_scoped_;
+};
+TEST_F(ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage,
+ ExtensionSortingGetMinOrMaxAppLaunchOrdinalsOnPage) {}
diff --git a/chrome/browser/ui/views/aura/app_list/extension_app_item.cc b/chrome/browser/ui/views/aura/app_list/extension_app_item.cc
index 1a431e7..37cdcae 100644
--- a/chrome/browser/ui/views/aura/app_list/extension_app_item.cc
+++ b/chrome/browser/ui/views/aura/app_list/extension_app_item.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -8,6 +8,7 @@
#include "chrome/browser/event_disposition.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/extensions/extension.h"
@@ -22,18 +23,19 @@
namespace {
-const ExtensionPrefs* GetExtensionPrefs(Profile* profile) {
- return profile->GetExtensionService()->extension_prefs();
+const ExtensionSorting* GetExtensionSorting(Profile* profile) {
+ return profile->GetExtensionService()->extension_prefs()->extension_sorting();
}
// Gets page ordinal of given |extension| in its app launch page.
StringOrdinal GetExtensionPageOrdinal(Profile* profile,
const Extension* extension) {
- const ExtensionPrefs* prefs = GetExtensionPrefs(profile);
- StringOrdinal page_ordinal = prefs->GetPageOrdinal(extension->id());
+ const ExtensionSorting* sorting = GetExtensionSorting(profile);
+ StringOrdinal page_ordinal = sorting->GetPageOrdinal(extension->id());
if (!page_ordinal.IsValid()) {
page_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
- prefs->CreateFirstAppPageOrdinal() : prefs->GetNaturalAppPageOrdinal();
+ sorting->CreateFirstAppPageOrdinal() :
+ sorting->GetNaturalAppPageOrdinal();
}
return page_ordinal;
@@ -42,21 +44,21 @@ StringOrdinal GetExtensionPageOrdinal(Profile* profile,
// Gets page index from page ordinal.
int GetExtensionPageIndex(Profile* profile,
const Extension* extension) {
- return std::max(0, GetExtensionPrefs(profile)->PageStringOrdinalAsInteger(
+ return std::max(0, GetExtensionSorting(profile)->PageStringOrdinalAsInteger(
GetExtensionPageOrdinal(profile, extension)));
}
// Gets app launch ordinal of given extension.
StringOrdinal GetExtensionLaunchOrdinal(Profile* profile,
const Extension* extension) {
- const ExtensionPrefs* prefs = GetExtensionPrefs(profile);
+ const ExtensionSorting* sorting = GetExtensionSorting(profile);
StringOrdinal app_launch_ordinal =
- prefs->GetAppLaunchOrdinal(extension->id());
+ sorting->GetAppLaunchOrdinal(extension->id());
if (!app_launch_ordinal.IsValid()) {
StringOrdinal page_ordinal = GetExtensionPageOrdinal(profile, extension);
app_launch_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
- prefs->CreateFirstAppLaunchOrdinal(page_ordinal) :
- prefs->CreateNextAppLaunchOrdinal(page_ordinal);
+ sorting->CreateFirstAppLaunchOrdinal(page_ordinal) :
+ sorting->CreateNextAppLaunchOrdinal(page_ordinal);
}
return app_launch_ordinal;
}
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index ae106c1..5f3684b 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -22,6 +22,7 @@
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sorting.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/profiles/profile.h"
@@ -156,7 +157,6 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension,
extension->id() == extension_misc::kWebStoreAppId);
if (extension->HasAPIPermission(ExtensionAPIPermission::kAppNotifications)) {
- ExtensionPrefs* prefs = service->extension_prefs();
value->SetBoolean("notifications_disabled",
prefs->IsAppNotificationDisabled(extension->id()));
}
@@ -164,30 +164,33 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension,
if (notification)
value->Set("notification", SerializeNotification(*notification));
- StringOrdinal page_ordinal = prefs->GetPageOrdinal(extension->id());
+ ExtensionSorting* sorting = prefs->extension_sorting();
+ StringOrdinal page_ordinal = sorting->GetPageOrdinal(extension->id());
if (!page_ordinal.IsValid()) {
// Make sure every app has a page ordinal (some predate the page ordinal).
// The webstore app should be on the first page.
page_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
- prefs->CreateFirstAppPageOrdinal() : prefs->GetNaturalAppPageOrdinal();
- prefs->SetPageOrdinal(extension->id(), page_ordinal);
+ sorting->CreateFirstAppPageOrdinal() :
+ sorting->GetNaturalAppPageOrdinal();
+ sorting->SetPageOrdinal(extension->id(), page_ordinal);
}
// We convert the page_ordinal to an integer because the pages are referenced
// from within an array in the javascript code, which can't be easily
// changed to handle the StringOrdinal values, so we do the conversion here.
- int page_index = prefs->PageStringOrdinalAsInteger(page_ordinal);
+ int page_index =
+ sorting->PageStringOrdinalAsInteger(page_ordinal);
value->SetInteger("page_index", page_index >= 0 ? page_index : 0);
StringOrdinal app_launch_ordinal =
- prefs->GetAppLaunchOrdinal(extension->id());
+ sorting->GetAppLaunchOrdinal(extension->id());
if (!app_launch_ordinal.IsValid()) {
// Make sure every app has a launch ordinal (some predate the launch
// ordinal). The webstore's app launch ordinal is always set to the first
// position.
app_launch_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
- prefs->CreateFirstAppLaunchOrdinal(page_ordinal) :
- prefs->CreateNextAppLaunchOrdinal(page_ordinal);
- prefs->SetAppLaunchOrdinal(extension->id(), app_launch_ordinal);
+ sorting->CreateFirstAppLaunchOrdinal(page_ordinal) :
+ sorting->CreateNextAppLaunchOrdinal(page_ordinal);
+ sorting->SetAppLaunchOrdinal(extension->id(), app_launch_ordinal);
}
value->SetString("app_launch_ordinal", app_launch_ordinal.ToString());
}
@@ -354,9 +357,10 @@ void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) {
// page index for non-app extensions. Old profiles can persist this error,
// and this fixes it. This caused GetNaturalAppPageIndex() to break
// (see http://crbug.com/98325) before it was an ordinal value.
- ExtensionPrefs* prefs = extension_service_->extension_prefs();
- if (prefs->GetPageOrdinal(extension->id()).IsValid())
- prefs->ClearPageOrdinal(extension->id());
+ ExtensionSorting* sortings =
+ extension_service_->extension_prefs()->extension_sorting();
+ if (sortings->GetPageOrdinal(extension->id()).IsValid())
+ sortings->ClearPageOrdinal(extension->id());
}
}
@@ -491,7 +495,8 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) {
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
- content::Source<ExtensionPrefs>(extension_service_->extension_prefs()));
+ content::Source<ExtensionSorting>(
+ extension_service_->extension_prefs()->extension_sorting()));
registrar_.Add(this, chrome::NOTIFICATION_WEB_STORE_PROMO_LOADED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
@@ -676,18 +681,20 @@ void AppLauncherHandler::HandleReorderApps(const ListValue* args) {
}
void AppLauncherHandler::HandleSetPageIndex(const ListValue* args) {
+ ExtensionSorting* extension_sorting =
+ extension_service_->extension_prefs()->extension_sorting();
+
std::string extension_id;
double page_index;
CHECK(args->GetString(0, &extension_id));
CHECK(args->GetDouble(1, &page_index));
const StringOrdinal& page_ordinal =
- extension_service_->extension_prefs()->PageIntegerAsStringOrdinal(
+ extension_sorting->PageIntegerAsStringOrdinal(
static_cast<size_t>(page_index));
// Don't update the page; it already knows the apps have been reordered.
AutoReset<bool> auto_reset(&ignore_changes_, true);
- extension_service_->extension_prefs()->SetPageOrdinal(extension_id,
- page_ordinal);
+ extension_sorting->SetPageOrdinal(extension_id, page_ordinal);
}
void AppLauncherHandler::HandlePromoSeen(const ListValue* args) {
@@ -720,8 +727,10 @@ void AppLauncherHandler::HandleGenerateAppForLink(const ListValue* args) {
double page_index;
CHECK(args->GetDouble(2, &page_index));
+ ExtensionSorting* extension_sorting =
+ extension_service_->extension_prefs()->extension_sorting();
const StringOrdinal& page_ordinal =
- extension_service_->extension_prefs()->PageIntegerAsStringOrdinal(
+ extension_sorting->PageIntegerAsStringOrdinal(
static_cast<size_t>(page_index));
Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 47b0215..33edee8 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1140,8 +1140,11 @@
'browser/extensions/extension_proxy_api_constants.h',
'browser/extensions/extension_proxy_api_helpers.cc',
'browser/extensions/extension_proxy_api_helpers.h',
+ 'browser/extensions/extension_scoped_prefs.h',
'browser/extensions/extension_service.cc',
'browser/extensions/extension_service.h',
+ 'browser/extensions/extension_sorting.cc',
+ 'browser/extensions/extension_sorting.h',
'browser/extensions/extension_special_storage_policy.cc',
'browser/extensions/extension_special_storage_policy.h',
'browser/extensions/extension_sync_data.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 62c73d4..092601b 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1427,10 +1427,12 @@
'browser/extensions/extension_permissions_api_helpers_unittest.cc',
'browser/extensions/extension_pref_value_map_unittest.cc',
'browser/extensions/extension_prefs_unittest.cc',
+ 'browser/extensions/extension_prefs_unittest.h',
'browser/extensions/extension_process_manager_unittest.cc',
'browser/extensions/extension_proxy_api_helpers_unittest.cc',
'browser/extensions/extension_service_unittest.cc',
'browser/extensions/extension_service_unittest.h',
+ 'browser/extensions/extension_sorting_unittest.cc',
'browser/extensions/extension_special_storage_policy_unittest.cc',
'browser/extensions/extension_sync_data_unittest.cc',
'browser/extensions/extension_tts_api_controller_unittest.cc',