1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/extensions/chrome_app_sorting.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_scoped_prefs.h"
121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/browser/extensions/extension_sync_service.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/extensions/default_app_order.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace extensions {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The number of apps per page. This isn't a hard limit, but new apps installed
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// from the webstore will overflow onto a new page if this limit is reached.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t kNaturalAppPageSize = 18;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A preference determining the order of which the apps appear on the NTP.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPrefAppLaunchOrdinal[] = "app_launcher_ordinal";
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A preference determining the page on which an app appears in the NTP.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPrefPageIndexDeprecated[] = "page_index";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kPrefPageOrdinal[] = "page_ordinal";
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ChromeAppSorting::AppOrdinals
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ChromeAppSorting::AppOrdinals::AppOrdinals() {}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ChromeAppSorting::AppOrdinals::~AppOrdinals() {}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ChromeAppSorting
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ChromeAppSorting::ChromeAppSorting()
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : extension_scoped_prefs_(NULL),
511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      extension_sync_service_(NULL),
5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      default_ordinals_created_(false) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ChromeAppSorting::~ChromeAppSorting() {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::SetExtensionScopedPrefs(ExtensionScopedPrefs* prefs) {
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  extension_scoped_prefs_ = prefs;
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::SetExtensionSyncService(
631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    ExtensionSyncService* extension_sync_service) {
641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  extension_sync_service_ = extension_sync_service;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::Initialize(
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::ExtensionIdList& extension_ids) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitializePageOrdinalMap(extension_ids);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MigrateAppIndex(extension_ids);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::CreateOrdinalsIfNecessary(size_t minimum_size) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create StringOrdinal values as required to ensure |ntp_ordinal_map_| has at
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // least |minimum_size| entries.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ntp_ordinal_map_.empty() && minimum_size > 0)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ntp_ordinal_map_[syncer::StringOrdinal::CreateInitialOrdinal()];
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (ntp_ordinal_map_.size() < minimum_size) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::StringOrdinal filler =
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ntp_ordinal_map_.rbegin()->first.CreateAfter();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AppLaunchOrdinalMap empty_ordinal_map;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ntp_ordinal_map_.insert(std::make_pair(filler, empty_ordinal_map));
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::MigrateAppIndex(
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::ExtensionIdList& extension_ids) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension_ids.empty())
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convert all the page index values to page ordinals. If there are any
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // app launch values that need to be migrated, inserted them into a sorted
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // set to be dealt with later.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::map<syncer::StringOrdinal, std::map<int, const std::string*>,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   syncer::StringOrdinal::LessThanFn> AppPositionToIdMapping;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppPositionToIdMapping app_launches_to_convert;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (extensions::ExtensionIdList::const_iterator ext_id =
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int old_page_index = 0;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::StringOrdinal page = GetPageOrdinal(*ext_id);
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (extension_scoped_prefs_->ReadPrefAsInteger(
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            *ext_id,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kPrefPageIndexDeprecated,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            &old_page_index)) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Some extensions have invalid page index, so we don't
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // attempt to convert them.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (old_page_index < 0) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DLOG(WARNING) << "Extension " << *ext_id
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      << " has an invalid page index " << old_page_index
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      << ". Aborting attempt to convert its index.";
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateOrdinalsIfNecessary(static_cast<size_t>(old_page_index) + 1);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      page = PageIntegerAsStringOrdinal(old_page_index);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetPageOrdinal(*ext_id, page);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_scoped_prefs_->UpdateExtensionPref(
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *ext_id, kPrefPageIndexDeprecated, NULL);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int old_app_launch_index = 0;
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (extension_scoped_prefs_->ReadPrefAsInteger(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            *ext_id,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kPrefAppLaunchIndexDeprecated,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            &old_app_launch_index)) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We can't update the app launch index value yet, because we use
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // GetNextAppLaunchOrdinal to get the new ordinal value and it requires
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // all the ordinals with lower values to have already been migrated.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // A valid page ordinal is also required because otherwise there is
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // no page to add the app to.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (page.IsValid())
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        app_launches_to_convert[page][old_app_launch_index] = &*ext_id;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_scoped_prefs_->UpdateExtensionPref(
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *ext_id, kPrefAppLaunchIndexDeprecated, NULL);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove any empty pages that may have been added. This shouldn't occur,
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but double check here to prevent future problems with conversions between
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // integers and StringOrdinals.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (PageOrdinalMap::iterator it = ntp_ordinal_map_.begin();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != ntp_ordinal_map_.end();) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->second.empty()) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PageOrdinalMap::iterator prev_it = it;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++it;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ntp_ordinal_map_.erase(prev_it);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++it;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (app_launches_to_convert.empty())
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the new app launch ordinals and remove the old preferences. Since
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the set is sorted, each time we migrate an apps index, we know that all of
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the remaining apps will appear further down the NTP than it or on a
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // different page.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (AppPositionToIdMapping::const_iterator page_it =
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           app_launches_to_convert.begin();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       page_it != app_launches_to_convert.end(); ++page_it) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::StringOrdinal page = page_it->first;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (std::map<int, const std::string*>::const_iterator launch_it =
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            page_it->second.begin(); launch_it != page_it->second.end();
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++launch_it) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetAppLaunchOrdinal(*(launch_it->second),
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          CreateNextAppLaunchOrdinal(page));
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::FixNTPOrdinalCollisions() {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (PageOrdinalMap::iterator page_it = ntp_ordinal_map_.begin();
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       page_it != ntp_ordinal_map_.end(); ++page_it) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AppLaunchOrdinalMap& page = page_it->second;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AppLaunchOrdinalMap::iterator app_launch_it = page.begin();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (app_launch_it != page.end()) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int app_count = page.count(app_launch_it->first);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (app_count == 1) {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ++app_launch_it;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::StringOrdinal repeated_ordinal = app_launch_it->first;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Sort the conflicting keys by their extension id, this is how
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the order is decided.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::vector<std::string> conflicting_ids;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (int i = 0; i < app_count; ++i, ++app_launch_it)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        conflicting_ids.push_back(app_launch_it->second);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::sort(conflicting_ids.begin(), conflicting_ids.end());
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::StringOrdinal upper_bound_ordinal = app_launch_it == page.end() ?
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          syncer::StringOrdinal() :
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          app_launch_it->first;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      syncer::StringOrdinal lower_bound_ordinal = repeated_ordinal;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Start at position 1 because the first extension can keep the conflicted
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // value.
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (int i = 1; i < app_count; ++i) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        syncer::StringOrdinal unique_app_launch;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (upper_bound_ordinal.IsValid()) {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          unique_app_launch =
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              lower_bound_ordinal.CreateBetween(upper_bound_ordinal);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          unique_app_launch = lower_bound_ordinal.CreateAfter();
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetAppLaunchOrdinal(conflicting_ids[i], unique_app_launch);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lower_bound_ordinal = unique_app_launch;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      content::Source<ChromeAppSorting>(this),
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::NotificationService::NoDetails());
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::EnsureValidOrdinals(
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& suggested_page) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal page_ordinal = GetPageOrdinal(extension_id);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!page_ordinal.IsValid()) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (suggested_page.IsValid()) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      page_ordinal = suggested_page;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!GetDefaultOrdinals(extension_id, &page_ordinal, NULL) ||
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !page_ordinal.IsValid()) {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      page_ordinal = GetNaturalAppPageOrdinal();
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetPageOrdinal(extension_id, page_ordinal);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal app_launch_ordinal = GetAppLaunchOrdinal(extension_id);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!app_launch_ordinal.IsValid()) {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If using default app launcher ordinal, make sure there is no collision.
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (GetDefaultOrdinals(extension_id, NULL, &app_launch_ordinal) &&
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        app_launch_ordinal.IsValid())
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      app_launch_ordinal = ResolveCollision(page_ordinal, app_launch_ordinal);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      app_launch_ordinal = CreateNextAppLaunchOrdinal(page_ordinal);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetAppLaunchOrdinal(extension_id, app_launch_ordinal);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::OnExtensionMoved(
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& moved_extension_id,
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& predecessor_extension_id,
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& successor_extension_id) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only need to change the StringOrdinal if there are neighbours.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!predecessor_extension_id.empty() || !successor_extension_id.empty()) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (predecessor_extension_id.empty()) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only a successor.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetAppLaunchOrdinal(
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          moved_extension_id,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetAppLaunchOrdinal(successor_extension_id).CreateBefore());
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (successor_extension_id.empty()) {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only a predecessor.
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetAppLaunchOrdinal(
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          moved_extension_id,
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetAppLaunchOrdinal(predecessor_extension_id).CreateAfter());
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Both a successor and predecessor
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const syncer::StringOrdinal& predecessor_ordinal =
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetAppLaunchOrdinal(predecessor_extension_id);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const syncer::StringOrdinal& successor_ordinal =
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetAppLaunchOrdinal(successor_extension_id);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetAppLaunchOrdinal(moved_extension_id,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          predecessor_ordinal.CreateBetween(successor_ordinal));
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
283f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      content::Source<ChromeAppSorting>(this),
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Details<const std::string>(&moved_extension_id));
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
288f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::GetAppLaunchOrdinal(
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id) const {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string raw_value;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the preference read fails then raw_value will still be unset and we
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will return an invalid StringOrdinal to signal that no app launch ordinal
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // was found.
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extension_scoped_prefs_->ReadPrefAsString(
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, kPrefAppLaunchOrdinal, &raw_value);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return syncer::StringOrdinal(raw_value);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::SetAppLaunchOrdinal(
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& new_app_launch_ordinal) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No work is required if the old and new values are the same.
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_app_launch_ordinal.EqualsOrBothInvalid(
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetAppLaunchOrdinal(extension_id))) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal page_ordinal = GetPageOrdinal(extension_id);
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveOrdinalMapping(
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, page_ordinal, GetAppLaunchOrdinal(extension_id));
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddOrdinalMapping(extension_id, page_ordinal, new_app_launch_ordinal);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Value* new_value = new_app_launch_ordinal.IsValid() ?
3143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      new base::StringValue(new_app_launch_ordinal.ToInternalValue()) :
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_scoped_prefs_->UpdateExtensionPref(
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kPrefAppLaunchOrdinal,
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_value);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SyncIfNeeded(extension_id);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
324f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::CreateFirstAppLaunchOrdinal(
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal) const {
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const syncer::StringOrdinal& min_ordinal =
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
328f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                         ChromeAppSorting::MIN_ORDINAL);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (min_ordinal.IsValid())
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return min_ordinal.CreateBefore();
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return syncer::StringOrdinal::CreateInitialOrdinal();
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::CreateNextAppLaunchOrdinal(
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal) const {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const syncer::StringOrdinal& max_ordinal =
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
340f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                         ChromeAppSorting::MAX_ORDINAL);
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (max_ordinal.IsValid())
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return max_ordinal.CreateAfter();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return syncer::StringOrdinal::CreateInitialOrdinal();
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::CreateFirstAppPageOrdinal() const {
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ntp_ordinal_map_.empty())
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return syncer::StringOrdinal::CreateInitialOrdinal();
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ntp_ordinal_map_.begin()->first;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::GetNaturalAppPageOrdinal() const {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ntp_ordinal_map_.empty())
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return syncer::StringOrdinal::CreateInitialOrdinal();
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (PageOrdinalMap::const_iterator it = ntp_ordinal_map_.begin();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != ntp_ordinal_map_.end(); ++it) {
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (CountItemsVisibleOnNtp(it->second) < kNaturalAppPageSize)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return it->first;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a new page as all existing pages are full.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal last_element = ntp_ordinal_map_.rbegin()->first;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return last_element.CreateAfter();
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
370f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::GetPageOrdinal(
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id) const {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string raw_data;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the preference read fails then raw_data will still be unset and we will
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // return an invalid StringOrdinal to signal that no page ordinal was found.
375c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extension_scoped_prefs_->ReadPrefAsString(
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, kPrefPageOrdinal, &raw_data);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return syncer::StringOrdinal(raw_data);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::SetPageOrdinal(
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& new_page_ordinal) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No work is required if the old and new values are the same.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (new_page_ordinal.EqualsOrBothInvalid(GetPageOrdinal(extension_id)))
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal app_launch_ordinal = GetAppLaunchOrdinal(extension_id);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveOrdinalMapping(
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, GetPageOrdinal(extension_id), app_launch_ordinal);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddOrdinalMapping(extension_id, new_page_ordinal, app_launch_ordinal);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Value* new_value = new_page_ordinal.IsValid() ?
3933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      new base::StringValue(new_page_ordinal.ToInternalValue()) :
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_scoped_prefs_->UpdateExtensionPref(
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kPrefPageOrdinal,
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_value);
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SyncIfNeeded(extension_id);
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
403f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::ClearOrdinals(const std::string& extension_id) {
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveOrdinalMapping(extension_id,
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       GetPageOrdinal(extension_id),
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       GetAppLaunchOrdinal(extension_id));
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_scoped_prefs_->UpdateExtensionPref(
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, kPrefPageOrdinal, NULL);
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_scoped_prefs_->UpdateExtensionPref(
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, kPrefAppLaunchOrdinal, NULL);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
414f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int ChromeAppSorting::PageStringOrdinalAsInteger(
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal) const {
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!page_ordinal.IsValid())
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PageOrdinalMap::const_iterator it = ntp_ordinal_map_.find(page_ordinal);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return it != ntp_ordinal_map_.end() ?
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::distance(ntp_ordinal_map_.begin(), it) : -1;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
424f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::PageIntegerAsStringOrdinal(
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t page_index) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page_index < ntp_ordinal_map_.size()) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PageOrdinalMap::const_iterator it = ntp_ordinal_map_.begin();
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::advance(it, page_index);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return it->first;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateOrdinalsIfNecessary(page_index + 1);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ntp_ordinal_map_.rbegin()->first;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
436f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::MarkExtensionAsHidden(const std::string& extension_id) {
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ntp_hidden_extensions_.insert(extension_id);
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
440f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::GetMinOrMaxAppLaunchOrdinalsOnPage(
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& target_page_ordinal,
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AppLaunchOrdinalReturn return_type) const {
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(target_page_ordinal.IsValid());
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal return_value;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PageOrdinalMap::const_iterator page =
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ntp_ordinal_map_.find(target_page_ordinal);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page != ntp_ordinal_map_.end()) {
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AppLaunchOrdinalMap& app_list = page->second;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (app_list.empty())
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return syncer::StringOrdinal();
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (return_type == ChromeAppSorting::MAX_ORDINAL)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_value = app_list.rbegin()->first;
457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    else if (return_type == ChromeAppSorting::MIN_ORDINAL)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return_value = app_list.begin()->first;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return return_value;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
464f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::InitializePageOrdinalMap(
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::ExtensionIdList& extension_ids) {
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (extensions::ExtensionIdList::const_iterator ext_it =
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           extension_ids.begin(); ext_it != extension_ids.end(); ++ext_it) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddOrdinalMapping(*ext_it,
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      GetPageOrdinal(*ext_it),
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      GetAppLaunchOrdinal(*ext_it));
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ensure that the web store app still isn't found in this list, since
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // it is added after this loop.
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(*ext_it != extension_misc::kWebStoreAppId);
475eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DCHECK(*ext_it != extension_misc::kChromeAppId);
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Include the Web Store App since it is displayed on the NTP.
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal web_store_app_page =
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetPageOrdinal(extension_misc::kWebStoreAppId);
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (web_store_app_page.IsValid()) {
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddOrdinalMapping(extension_misc::kWebStoreAppId,
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      web_store_app_page,
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      GetAppLaunchOrdinal(extension_misc::kWebStoreAppId));
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
486eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Include the Chrome App since it is displayed in the app launcher.
487eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  syncer::StringOrdinal chrome_app_page =
488eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      GetPageOrdinal(extension_misc::kChromeAppId);
489eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (chrome_app_page.IsValid()) {
490eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    AddOrdinalMapping(extension_misc::kChromeAppId,
491eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                      chrome_app_page,
492eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                      GetAppLaunchOrdinal(extension_misc::kChromeAppId));
493eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
496f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::AddOrdinalMapping(
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal,
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& app_launch_ordinal) {
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!page_ordinal.IsValid() || !app_launch_ordinal.IsValid())
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ntp_ordinal_map_[page_ordinal].insert(
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::make_pair(app_launch_ordinal, extension_id));
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
507f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::RemoveOrdinalMapping(
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal,
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& app_launch_ordinal) {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!page_ordinal.IsValid() || !app_launch_ordinal.IsValid())
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that the page exists using find to prevent creating a new page
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // if |page_ordinal| isn't a used page.
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PageOrdinalMap::iterator page_map = ntp_ordinal_map_.find(page_ordinal);
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page_map == ntp_ordinal_map_.end())
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (AppLaunchOrdinalMap::iterator it =
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           page_map->second.find(app_launch_ordinal);
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != page_map->second.end(); ++it) {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->second == extension_id) {
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      page_map->second.erase(it);
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
530f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::SyncIfNeeded(const std::string& extension_id) {
5311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (extension_sync_service_)
5321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    extension_sync_service_->SyncOrderingChange(extension_id);
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ChromeAppSorting::CreateDefaultOrdinals() {
53690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (default_ordinals_created_)
53790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
53890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  default_ordinals_created_ = true;
53990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The following defines the default order of apps.
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS)
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> app_ids;
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chromeos::default_app_order::Get(&app_ids);
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* kDefaultAppOrder[] = {
546eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    extension_misc::kChromeAppId,
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_misc::kWebStoreAppId,
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::vector<const char*> app_ids(
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kDefaultAppOrder, kDefaultAppOrder + arraysize(kDefaultAppOrder));
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal page_ordinal = CreateFirstAppPageOrdinal();
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  syncer::StringOrdinal app_launch_ordinal =
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateFirstAppLaunchOrdinal(page_ordinal);
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < app_ids.size(); ++i) {
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string extension_id = app_ids[i];
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default_ordinals_[extension_id].page_ordinal = page_ordinal;
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default_ordinals_[extension_id].app_launch_ordinal = app_launch_ordinal;
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    app_launch_ordinal = app_launch_ordinal.CreateAfter();
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
564f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool ChromeAppSorting::GetDefaultOrdinals(
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    syncer::StringOrdinal* page_ordinal,
56790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    syncer::StringOrdinal* app_launch_ordinal) {
56890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  CreateDefaultOrdinals();
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppOrdinalsMap::const_iterator it = default_ordinals_.find(extension_id);
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it == default_ordinals_.end())
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page_ordinal)
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *page_ordinal = it->second.page_ordinal;
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (app_launch_ordinal)
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *app_launch_ordinal = it->second.app_launch_ordinal;
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
580f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)syncer::StringOrdinal ChromeAppSorting::ResolveCollision(
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& page_ordinal,
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const syncer::StringOrdinal& app_launch_ordinal) const {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(page_ordinal.IsValid() && app_launch_ordinal.IsValid());
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PageOrdinalMap::const_iterator page_it = ntp_ordinal_map_.find(page_ordinal);
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page_it == ntp_ordinal_map_.end())
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return app_launch_ordinal;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AppLaunchOrdinalMap& page = page_it->second;
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AppLaunchOrdinalMap::const_iterator app_it = page.find(app_launch_ordinal);
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (app_it == page.end())
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return app_launch_ordinal;
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finds the next app launcher ordinal. This is done by the following loop
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because this function could be called before FixNTPOrdinalCollisions and
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // thus |page| might contains multiple entries with the same app launch
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ordinal. See http://crbug.com/155603
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (app_it != page.end() && app_launch_ordinal.Equals(app_it->first))
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++app_it;
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is no next after the collision, returns the next ordinal.
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (app_it == page.end())
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return app_launch_ordinal.CreateAfter();
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Otherwise, returns the ordinal between the collision and the next ordinal.
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return app_launch_ordinal.CreateBetween(app_it->first);
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
609f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)size_t ChromeAppSorting::CountItemsVisibleOnNtp(
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const AppLaunchOrdinalMap& m) const {
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t result = 0;
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (AppLaunchOrdinalMap::const_iterator it = m.begin(); it != m.end();
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       ++it) {
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& id = it->second;
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (ntp_hidden_extensions_.count(id) == 0)
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      result++;
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return result;
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
620f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
621f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace extensions
622