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
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 493ba5ad..2bc2dd13 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 @@
 
 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 @@
 // 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 @@
     : 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::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::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::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 @@
 
   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::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 @@
   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 @@
   }
 
   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 @@
 //       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 @@
                               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 @@
   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 @@
   // 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 @@
       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 @@
   // 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 @@
   // 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 66dfbfde..d43bd55e 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 @@
   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_) {
-  }
+ExtensionPrefsTest::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;
+ExtensionPrefsTest::~ExtensionPrefsTest() {}
 
-  // 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;
+void ExtensionPrefsTest::RegisterPreferences() {}
 
-  // This function is called to Register preference default values.
-  virtual void RegisterPreferences() {}
+void ExtensionPrefsTest::SetUp() {
+  RegisterPreferences();
+  Initialize();
+}
 
-  virtual void SetUp() {
-    RegisterPreferences();
-    Initialize();
-  }
+void ExtensionPrefsTest::TearDown() {
+  Verify();
 
-  virtual void TearDown() {
-    Verify();
-
-    // Reset ExtensionPrefs, and re-verify.
-    prefs_.RecreateExtensionPrefs();
-    RegisterPreferences();
-    Verify();
-  }
-
- 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);
-};
+  // Reset ExtensionPrefs, and re-verify.
+  prefs_.RecreateExtensionPrefs();
+  RegisterPreferences();
+  Verify();
+}
 
 // Tests the LastPingDay/SetLastPingDay functions.
 class ExtensionPrefsLastPingDay : public ExtensionPrefsTest {
@@ -670,116 +644,6 @@
 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 @@
 
 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;
+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");
+  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_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();
+  ext1_ = ext1_scoped_.get();
+  ext2_ = ext2_scoped_.get();
+  ext3_ = ext3_scoped_.get();
 
-    for (size_t i = 0; i < arraysize(installed); ++i)
+  for (size_t i = 0; i < arraysize(installed); ++i)
+    installed[i] = false;
+}
+
+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 ExtensionPrefsPrepopulatedTest::InstallExtControlledPref(
+    Extension *ext,
+    const std::string& key,
+    Value* val) {
+  EnsureExtensionInstalled(ext);
+  prefs()->SetExtensionControlledPref(
+      ext->id(), key, kExtensionPrefsScopeRegular, val);
+}
+
+void ExtensionPrefsPrepopulatedTest::InstallExtControlledPrefIncognito(
+    Extension *ext,
+    const std::string& key,
+    Value* val) {
+  EnsureExtensionInstalled(ext);
+  prefs()->SetExtensionControlledPref(
+      ext->id(), key, kExtensionPrefsScopeIncognitoPersistent, val);
+}
+
+void ExtensionPrefsPrepopulatedTest
+::InstallExtControlledPrefIncognitoSessionOnly(Extension *ext,
+                                               const std::string& key,
+                                               Value* val) {
+  EnsureExtensionInstalled(ext);
+  prefs()->SetExtensionControlledPref(
+      ext->id(), key, kExtensionPrefsScopeIncognitoSessionOnly, val);
+}
+
+void ExtensionPrefsPrepopulatedTest::InstallExtension(Extension *ext) {
+  EnsureExtensionInstalled(ext);
+}
+
+void ExtensionPrefsPrepopulatedTest::UninstallExtension(
+    const std::string& extension_id) {
+  EnsureExtensionUninstalled(extension_id);
+}
+
+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 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;
-  }
-
-  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);
-  }
-
-  void InstallExtControlledPrefIncognito(Extension *ext,
-                                         const std::string& key,
-                                         Value* val) {
-    EnsureExtensionInstalled(ext);
-    prefs()->SetExtensionControlledPref(
-        ext->id(), key, kExtensionPrefsScopeIncognitoPersistent, val);
-  }
-
-  void InstallExtControlledPrefIncognitoSessionOnly(
-      Extension *ext,
-      const std::string& key,
-      Value* val) {
-    EnsureExtensionInstalled(ext);
-    prefs()->SetExtensionControlledPref(
-        ext->id(), key, kExtensionPrefsScopeIncognitoSessionOnly, val);
-  }
-
-  void InstallExtension(Extension *ext) {
-    EnsureExtensionInstalled(ext);
-  }
-
-  void UninstallExtension(const std::string& extension_id) {
-    EnsureExtensionUninstalled(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) {
-    // 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;
-      }
+      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;
-      }
-    }
-    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 @@
 
 // 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 @@
 
 // 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 @@
 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 @@
   }
 };
 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 @@
     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 @@
 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 ExtensionPrefsSetExtensionControlledPref
-    : public ExtensionPrefsPreferencesBase {
+    : public ExtensionPrefsPrepopulatedTest {
  public:
   virtual void Initialize() {
     MockStringValue* v1 = new MockStringValue("https://www.chromium.org");
@@ -1219,8 +1069,7 @@
 
 // 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 @@
   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 @@
 
 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 @@
   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 @@
 
 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 @@
   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 @@
     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..0a7e397ce
--- /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 @@
 // 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 @@
       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 @@
   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 @@
       // 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 @@
     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::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 @@
 
   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());