Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/extensions/extension_sorting.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/browser/extensions/extension_scoped_prefs.h"
     12 #include "chrome/browser/extensions/extension_service.h"
     13 #include "chrome/common/extensions/extension.h"
     14 #include "content/public/browser/notification_service.h"
     15 
     16 #if defined(OS_CHROMEOS)
     17 #include "chrome/browser/chromeos/extensions/default_app_order.h"
     18 #endif
     19 
     20 using extensions::ExtensionPrefs;
     21 
     22 namespace {
     23 
     24 // The number of apps per page. This isn't a hard limit, but new apps installed
     25 // from the webstore will overflow onto a new page if this limit is reached.
     26 const size_t kNaturalAppPageSize = 18;
     27 
     28 // A preference determining the order of which the apps appear on the NTP.
     29 const char kPrefAppLaunchIndexDeprecated[] = "app_launcher_index";
     30 const char kPrefAppLaunchOrdinal[] = "app_launcher_ordinal";
     31 
     32 // A preference determining the page on which an app appears in the NTP.
     33 const char kPrefPageIndexDeprecated[] = "page_index";
     34 const char kPrefPageOrdinal[] = "page_ordinal";
     35 
     36 }  // namespace
     37 
     38 ////////////////////////////////////////////////////////////////////////////////
     39 // ExtensionSorting::AppOrdinals
     40 
     41 ExtensionSorting::AppOrdinals::AppOrdinals() {}
     42 
     43 ExtensionSorting::AppOrdinals::~AppOrdinals() {}
     44 
     45 ////////////////////////////////////////////////////////////////////////////////
     46 // ExtensionSorting
     47 
     48 ExtensionSorting::ExtensionSorting(ExtensionScopedPrefs* extension_scoped_prefs)
     49     : extension_scoped_prefs_(extension_scoped_prefs),
     50       extension_service_(NULL),
     51       default_ordinals_created_(false) {
     52 }
     53 
     54 ExtensionSorting::~ExtensionSorting() {
     55 }
     56 
     57 void ExtensionSorting::SetExtensionService(
     58     ExtensionServiceInterface* extension_service) {
     59   extension_service_ = extension_service;
     60 }
     61 
     62 void ExtensionSorting::Initialize(
     63     const extensions::ExtensionIdList& extension_ids) {
     64   InitializePageOrdinalMap(extension_ids);
     65 
     66   MigrateAppIndex(extension_ids);
     67 }
     68 
     69 void ExtensionSorting::CreateOrdinalsIfNecessary(size_t minimum_size) {
     70   // Create StringOrdinal values as required to ensure |ntp_ordinal_map_| has at
     71   // least |minimum_size| entries.
     72   if (ntp_ordinal_map_.empty() && minimum_size > 0)
     73     ntp_ordinal_map_[syncer::StringOrdinal::CreateInitialOrdinal()];
     74 
     75   while (ntp_ordinal_map_.size() < minimum_size) {
     76     syncer::StringOrdinal filler =
     77         ntp_ordinal_map_.rbegin()->first.CreateAfter();
     78     AppLaunchOrdinalMap empty_ordinal_map;
     79     ntp_ordinal_map_.insert(std::make_pair(filler, empty_ordinal_map));
     80   }
     81 }
     82 
     83 void ExtensionSorting::MigrateAppIndex(
     84     const extensions::ExtensionIdList& extension_ids) {
     85   if (extension_ids.empty())
     86     return;
     87 
     88   // Convert all the page index values to page ordinals. If there are any
     89   // app launch values that need to be migrated, inserted them into a sorted
     90   // set to be dealt with later.
     91   typedef std::map<syncer::StringOrdinal, std::map<int, const std::string*>,
     92                    syncer::StringOrdinal::LessThanFn> AppPositionToIdMapping;
     93   AppPositionToIdMapping app_launches_to_convert;
     94   for (extensions::ExtensionIdList::const_iterator ext_id =
     95            extension_ids.begin(); ext_id != extension_ids.end(); ++ext_id) {
     96     int old_page_index = 0;
     97     syncer::StringOrdinal page = GetPageOrdinal(*ext_id);
     98     if (extension_scoped_prefs_->ReadPrefAsInteger(
     99             *ext_id,
    100             kPrefPageIndexDeprecated,
    101             &old_page_index)) {
    102       // Some extensions have invalid page index, so we don't
    103       // attempt to convert them.
    104       if (old_page_index < 0) {
    105         DLOG(WARNING) << "Extension " << *ext_id
    106                       << " has an invalid page index " << old_page_index
    107                       << ". Aborting attempt to convert its index.";
    108         break;
    109       }
    110 
    111       CreateOrdinalsIfNecessary(static_cast<size_t>(old_page_index) + 1);
    112 
    113       page = PageIntegerAsStringOrdinal(old_page_index);
    114       SetPageOrdinal(*ext_id, page);
    115       extension_scoped_prefs_->UpdateExtensionPref(
    116           *ext_id, kPrefPageIndexDeprecated, NULL);
    117     }
    118 
    119     int old_app_launch_index = 0;
    120     if (extension_scoped_prefs_->ReadPrefAsInteger(
    121             *ext_id,
    122             kPrefAppLaunchIndexDeprecated,
    123             &old_app_launch_index)) {
    124       // We can't update the app launch index value yet, because we use
    125       // GetNextAppLaunchOrdinal to get the new ordinal value and it requires
    126       // all the ordinals with lower values to have already been migrated.
    127       // A valid page ordinal is also required because otherwise there is
    128       // no page to add the app to.
    129       if (page.IsValid())
    130         app_launches_to_convert[page][old_app_launch_index] = &*ext_id;
    131 
    132       extension_scoped_prefs_->UpdateExtensionPref(
    133           *ext_id, kPrefAppLaunchIndexDeprecated, NULL);
    134     }
    135   }
    136 
    137   // Remove any empty pages that may have been added. This shouldn't occur,
    138   // but double check here to prevent future problems with conversions between
    139   // integers and StringOrdinals.
    140   for (PageOrdinalMap::iterator it = ntp_ordinal_map_.begin();
    141        it != ntp_ordinal_map_.end();) {
    142     if (it->second.empty()) {
    143       PageOrdinalMap::iterator prev_it = it;
    144       ++it;
    145       ntp_ordinal_map_.erase(prev_it);
    146     } else {
    147       ++it;
    148     }
    149   }
    150 
    151   if (app_launches_to_convert.empty())
    152     return;
    153 
    154   // Create the new app launch ordinals and remove the old preferences. Since
    155   // the set is sorted, each time we migrate an apps index, we know that all of
    156   // the remaining apps will appear further down the NTP than it or on a
    157   // different page.
    158   for (AppPositionToIdMapping::const_iterator page_it =
    159            app_launches_to_convert.begin();
    160        page_it != app_launches_to_convert.end(); ++page_it) {
    161     syncer::StringOrdinal page = page_it->first;
    162     for (std::map<int, const std::string*>::const_iterator launch_it =
    163             page_it->second.begin(); launch_it != page_it->second.end();
    164         ++launch_it) {
    165       SetAppLaunchOrdinal(*(launch_it->second),
    166                           CreateNextAppLaunchOrdinal(page));
    167     }
    168   }
    169 }
    170 
    171 void ExtensionSorting::FixNTPOrdinalCollisions() {
    172   for (PageOrdinalMap::iterator page_it = ntp_ordinal_map_.begin();
    173        page_it != ntp_ordinal_map_.end(); ++page_it) {
    174     AppLaunchOrdinalMap& page = page_it->second;
    175 
    176     AppLaunchOrdinalMap::iterator app_launch_it = page.begin();
    177     while (app_launch_it != page.end()) {
    178       int app_count = page.count(app_launch_it->first);
    179       if (app_count == 1) {
    180         ++app_launch_it;
    181         continue;
    182       }
    183 
    184       syncer::StringOrdinal repeated_ordinal = app_launch_it->first;
    185 
    186       // Sort the conflicting keys by their extension id, this is how
    187       // the order is decided.
    188       std::vector<std::string> conflicting_ids;
    189       for (int i = 0; i < app_count; ++i, ++app_launch_it)
    190         conflicting_ids.push_back(app_launch_it->second);
    191       std::sort(conflicting_ids.begin(), conflicting_ids.end());
    192 
    193       syncer::StringOrdinal upper_bound_ordinal = app_launch_it == page.end() ?
    194           syncer::StringOrdinal() :
    195           app_launch_it->first;
    196       syncer::StringOrdinal lower_bound_ordinal = repeated_ordinal;
    197 
    198       // Start at position 1 because the first extension can keep the conflicted
    199       // value.
    200       for (int i = 1; i < app_count; ++i) {
    201         syncer::StringOrdinal unique_app_launch;
    202         if (upper_bound_ordinal.IsValid()) {
    203           unique_app_launch =
    204               lower_bound_ordinal.CreateBetween(upper_bound_ordinal);
    205         } else {
    206           unique_app_launch = lower_bound_ordinal.CreateAfter();
    207         }
    208 
    209         SetAppLaunchOrdinal(conflicting_ids[i], unique_app_launch);
    210         lower_bound_ordinal = unique_app_launch;
    211       }
    212     }
    213   }
    214 
    215   content::NotificationService::current()->Notify(
    216       chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
    217       content::Source<ExtensionSorting>(this),
    218       content::NotificationService::NoDetails());
    219 }
    220 
    221 void ExtensionSorting::EnsureValidOrdinals(
    222     const std::string& extension_id,
    223     const syncer::StringOrdinal& suggested_page) {
    224   syncer::StringOrdinal page_ordinal = GetPageOrdinal(extension_id);
    225   if (!page_ordinal.IsValid()) {
    226     if (suggested_page.IsValid()) {
    227       page_ordinal = suggested_page;
    228     } else if (!GetDefaultOrdinals(extension_id, &page_ordinal, NULL) ||
    229         !page_ordinal.IsValid()) {
    230       page_ordinal = GetNaturalAppPageOrdinal();
    231     }
    232 
    233     SetPageOrdinal(extension_id, page_ordinal);
    234   }
    235 
    236   syncer::StringOrdinal app_launch_ordinal = GetAppLaunchOrdinal(extension_id);
    237   if (!app_launch_ordinal.IsValid()) {
    238     // If using default app launcher ordinal, make sure there is no collision.
    239     if (GetDefaultOrdinals(extension_id, NULL, &app_launch_ordinal) &&
    240         app_launch_ordinal.IsValid())
    241       app_launch_ordinal = ResolveCollision(page_ordinal, app_launch_ordinal);
    242     else
    243       app_launch_ordinal = CreateNextAppLaunchOrdinal(page_ordinal);
    244 
    245     SetAppLaunchOrdinal(extension_id, app_launch_ordinal);
    246   }
    247 }
    248 
    249 void ExtensionSorting::OnExtensionMoved(
    250     const std::string& moved_extension_id,
    251     const std::string& predecessor_extension_id,
    252     const std::string& successor_extension_id) {
    253   // We only need to change the StringOrdinal if there are neighbours.
    254   if (!predecessor_extension_id.empty() || !successor_extension_id.empty()) {
    255     if (predecessor_extension_id.empty()) {
    256       // Only a successor.
    257       SetAppLaunchOrdinal(
    258           moved_extension_id,
    259           GetAppLaunchOrdinal(successor_extension_id).CreateBefore());
    260     } else if (successor_extension_id.empty()) {
    261       // Only a predecessor.
    262       SetAppLaunchOrdinal(
    263           moved_extension_id,
    264           GetAppLaunchOrdinal(predecessor_extension_id).CreateAfter());
    265     } else {
    266       // Both a successor and predecessor
    267       const syncer::StringOrdinal& predecessor_ordinal =
    268           GetAppLaunchOrdinal(predecessor_extension_id);
    269       const syncer::StringOrdinal& successor_ordinal =
    270           GetAppLaunchOrdinal(successor_extension_id);
    271       SetAppLaunchOrdinal(moved_extension_id,
    272                           predecessor_ordinal.CreateBetween(successor_ordinal));
    273     }
    274   }
    275 
    276   content::NotificationService::current()->Notify(
    277       chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
    278       content::Source<ExtensionSorting>(this),
    279       content::Details<const std::string>(&moved_extension_id));
    280 }
    281 
    282 
    283 syncer::StringOrdinal ExtensionSorting::GetAppLaunchOrdinal(
    284     const std::string& extension_id) const {
    285   std::string raw_value;
    286   // If the preference read fails then raw_value will still be unset and we
    287   // will return an invalid StringOrdinal to signal that no app launch ordinal
    288   // was found.
    289   extension_scoped_prefs_->ReadPrefAsString(
    290       extension_id, kPrefAppLaunchOrdinal, &raw_value);
    291   return syncer::StringOrdinal(raw_value);
    292 }
    293 
    294 void ExtensionSorting::SetAppLaunchOrdinal(
    295     const std::string& extension_id,
    296     const syncer::StringOrdinal& new_app_launch_ordinal) {
    297   // No work is required if the old and new values are the same.
    298   if (new_app_launch_ordinal.EqualsOrBothInvalid(
    299           GetAppLaunchOrdinal(extension_id))) {
    300     return;
    301   }
    302 
    303   syncer::StringOrdinal page_ordinal = GetPageOrdinal(extension_id);
    304   RemoveOrdinalMapping(
    305       extension_id, page_ordinal, GetAppLaunchOrdinal(extension_id));
    306   AddOrdinalMapping(extension_id, page_ordinal, new_app_launch_ordinal);
    307 
    308   Value* new_value = new_app_launch_ordinal.IsValid() ?
    309       Value::CreateStringValue(new_app_launch_ordinal.ToInternalValue()) :
    310       NULL;
    311 
    312   extension_scoped_prefs_->UpdateExtensionPref(
    313       extension_id,
    314       kPrefAppLaunchOrdinal,
    315       new_value);
    316   SyncIfNeeded(extension_id);
    317 }
    318 
    319 syncer::StringOrdinal ExtensionSorting::CreateFirstAppLaunchOrdinal(
    320     const syncer::StringOrdinal& page_ordinal) const {
    321   const syncer::StringOrdinal& min_ordinal =
    322       GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
    323                                          ExtensionSorting::MIN_ORDINAL);
    324 
    325   if (min_ordinal.IsValid())
    326     return min_ordinal.CreateBefore();
    327   else
    328     return syncer::StringOrdinal::CreateInitialOrdinal();
    329 }
    330 
    331 syncer::StringOrdinal ExtensionSorting::CreateNextAppLaunchOrdinal(
    332     const syncer::StringOrdinal& page_ordinal) const {
    333   const syncer::StringOrdinal& max_ordinal =
    334       GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal,
    335                                          ExtensionSorting::MAX_ORDINAL);
    336 
    337   if (max_ordinal.IsValid())
    338     return max_ordinal.CreateAfter();
    339   else
    340     return syncer::StringOrdinal::CreateInitialOrdinal();
    341 }
    342 
    343 syncer::StringOrdinal ExtensionSorting::CreateFirstAppPageOrdinal() const {
    344   if (ntp_ordinal_map_.empty())
    345     return syncer::StringOrdinal::CreateInitialOrdinal();
    346 
    347   return ntp_ordinal_map_.begin()->first;
    348 }
    349 
    350 syncer::StringOrdinal ExtensionSorting::GetNaturalAppPageOrdinal() const {
    351   if (ntp_ordinal_map_.empty())
    352     return syncer::StringOrdinal::CreateInitialOrdinal();
    353 
    354   for (PageOrdinalMap::const_iterator it = ntp_ordinal_map_.begin();
    355        it != ntp_ordinal_map_.end(); ++it) {
    356     if (CountItemsVisibleOnNtp(it->second) < kNaturalAppPageSize)
    357       return it->first;
    358   }
    359 
    360   // Add a new page as all existing pages are full.
    361   syncer::StringOrdinal last_element = ntp_ordinal_map_.rbegin()->first;
    362   return last_element.CreateAfter();
    363 }
    364 
    365 syncer::StringOrdinal ExtensionSorting::GetPageOrdinal(
    366     const std::string& extension_id) const {
    367   std::string raw_data;
    368   // If the preference read fails then raw_data will still be unset and we will
    369   // return an invalid StringOrdinal to signal that no page ordinal was found.
    370   extension_scoped_prefs_->ReadPrefAsString(
    371       extension_id, kPrefPageOrdinal, &raw_data);
    372   return syncer::StringOrdinal(raw_data);
    373 }
    374 
    375 void ExtensionSorting::SetPageOrdinal(
    376     const std::string& extension_id,
    377     const syncer::StringOrdinal& new_page_ordinal) {
    378   // No work is required if the old and new values are the same.
    379   if (new_page_ordinal.EqualsOrBothInvalid(GetPageOrdinal(extension_id)))
    380     return;
    381 
    382   syncer::StringOrdinal app_launch_ordinal = GetAppLaunchOrdinal(extension_id);
    383   RemoveOrdinalMapping(
    384       extension_id, GetPageOrdinal(extension_id), app_launch_ordinal);
    385   AddOrdinalMapping(extension_id, new_page_ordinal, app_launch_ordinal);
    386 
    387   Value* new_value = new_page_ordinal.IsValid() ?
    388       Value::CreateStringValue(new_page_ordinal.ToInternalValue()) :
    389       NULL;
    390 
    391   extension_scoped_prefs_->UpdateExtensionPref(
    392       extension_id,
    393       kPrefPageOrdinal,
    394       new_value);
    395   SyncIfNeeded(extension_id);
    396 }
    397 
    398 void ExtensionSorting::ClearOrdinals(const std::string& extension_id) {
    399   RemoveOrdinalMapping(extension_id,
    400                        GetPageOrdinal(extension_id),
    401                        GetAppLaunchOrdinal(extension_id));
    402 
    403   extension_scoped_prefs_->UpdateExtensionPref(
    404       extension_id, kPrefPageOrdinal, NULL);
    405   extension_scoped_prefs_->UpdateExtensionPref(
    406       extension_id, kPrefAppLaunchOrdinal, NULL);
    407 }
    408 
    409 int ExtensionSorting::PageStringOrdinalAsInteger(
    410     const syncer::StringOrdinal& page_ordinal) const {
    411   if (!page_ordinal.IsValid())
    412     return -1;
    413 
    414   PageOrdinalMap::const_iterator it = ntp_ordinal_map_.find(page_ordinal);
    415   return it != ntp_ordinal_map_.end() ?
    416       std::distance(ntp_ordinal_map_.begin(), it) : -1;
    417 }
    418 
    419 syncer::StringOrdinal ExtensionSorting::PageIntegerAsStringOrdinal(
    420     size_t page_index) {
    421   if (page_index < ntp_ordinal_map_.size()) {
    422     PageOrdinalMap::const_iterator it = ntp_ordinal_map_.begin();
    423     std::advance(it, page_index);
    424     return it->first;
    425   }
    426 
    427   CreateOrdinalsIfNecessary(page_index + 1);
    428   return ntp_ordinal_map_.rbegin()->first;
    429 }
    430 
    431 void ExtensionSorting::MarkExtensionAsHidden(const std::string& extension_id) {
    432   ntp_hidden_extensions_.insert(extension_id);
    433 }
    434 
    435 syncer::StringOrdinal ExtensionSorting::GetMinOrMaxAppLaunchOrdinalsOnPage(
    436     const syncer::StringOrdinal& target_page_ordinal,
    437     AppLaunchOrdinalReturn return_type) const {
    438   CHECK(target_page_ordinal.IsValid());
    439 
    440   syncer::StringOrdinal return_value;
    441 
    442   PageOrdinalMap::const_iterator page =
    443       ntp_ordinal_map_.find(target_page_ordinal);
    444   if (page != ntp_ordinal_map_.end()) {
    445     const AppLaunchOrdinalMap& app_list = page->second;
    446 
    447     if (app_list.empty())
    448       return syncer::StringOrdinal();
    449 
    450     if (return_type == ExtensionSorting::MAX_ORDINAL)
    451       return_value = app_list.rbegin()->first;
    452     else if (return_type == ExtensionSorting::MIN_ORDINAL)
    453       return_value = app_list.begin()->first;
    454   }
    455 
    456   return return_value;
    457 }
    458 
    459 void ExtensionSorting::InitializePageOrdinalMap(
    460     const extensions::ExtensionIdList& extension_ids) {
    461   for (extensions::ExtensionIdList::const_iterator ext_it =
    462            extension_ids.begin(); ext_it != extension_ids.end(); ++ext_it) {
    463     AddOrdinalMapping(*ext_it,
    464                       GetPageOrdinal(*ext_it),
    465                       GetAppLaunchOrdinal(*ext_it));
    466 
    467     // Ensure that the web store app still isn't found in this list, since
    468     // it is added after this loop.
    469     DCHECK(*ext_it != extension_misc::kWebStoreAppId);
    470     DCHECK(*ext_it != extension_misc::kChromeAppId);
    471   }
    472 
    473   // Include the Web Store App since it is displayed on the NTP.
    474   syncer::StringOrdinal web_store_app_page =
    475       GetPageOrdinal(extension_misc::kWebStoreAppId);
    476   if (web_store_app_page.IsValid()) {
    477     AddOrdinalMapping(extension_misc::kWebStoreAppId,
    478                       web_store_app_page,
    479                       GetAppLaunchOrdinal(extension_misc::kWebStoreAppId));
    480   }
    481   // Include the Chrome App since it is displayed in the app launcher.
    482   syncer::StringOrdinal chrome_app_page =
    483       GetPageOrdinal(extension_misc::kChromeAppId);
    484   if (chrome_app_page.IsValid()) {
    485     AddOrdinalMapping(extension_misc::kChromeAppId,
    486                       chrome_app_page,
    487                       GetAppLaunchOrdinal(extension_misc::kChromeAppId));
    488   }
    489 }
    490 
    491 void ExtensionSorting::AddOrdinalMapping(
    492     const std::string& extension_id,
    493     const syncer::StringOrdinal& page_ordinal,
    494     const syncer::StringOrdinal& app_launch_ordinal) {
    495   if (!page_ordinal.IsValid() || !app_launch_ordinal.IsValid())
    496     return;
    497 
    498   ntp_ordinal_map_[page_ordinal].insert(
    499       std::make_pair(app_launch_ordinal, extension_id));
    500 }
    501 
    502 void ExtensionSorting::RemoveOrdinalMapping(
    503     const std::string& extension_id,
    504     const syncer::StringOrdinal& page_ordinal,
    505     const syncer::StringOrdinal& app_launch_ordinal) {
    506   if (!page_ordinal.IsValid() || !app_launch_ordinal.IsValid())
    507     return;
    508 
    509   // Check that the page exists using find to prevent creating a new page
    510   // if |page_ordinal| isn't a used page.
    511   PageOrdinalMap::iterator page_map = ntp_ordinal_map_.find(page_ordinal);
    512   if (page_map == ntp_ordinal_map_.end())
    513     return;
    514 
    515   for (AppLaunchOrdinalMap::iterator it =
    516            page_map->second.find(app_launch_ordinal);
    517        it != page_map->second.end(); ++it) {
    518     if (it->second == extension_id) {
    519       page_map->second.erase(it);
    520       break;
    521     }
    522   }
    523 }
    524 
    525 void ExtensionSorting::SyncIfNeeded(const std::string& extension_id) {
    526   if (extension_service_) {
    527     const extensions::Extension* ext =
    528         extension_service_->GetInstalledExtension(extension_id);
    529 
    530     if (ext) {
    531       // It is possible for old extension to have ordinal values, but they
    532       // shouldn't so we clear them.
    533       if (!ext->is_app())
    534         ClearOrdinals(extension_id);
    535 
    536       extension_service_->SyncExtensionChangeIfNeeded(*ext);
    537     }
    538   }
    539 }
    540 
    541 void ExtensionSorting::CreateDefaultOrdinals() {
    542   if (default_ordinals_created_)
    543     return;
    544   default_ordinals_created_ = true;
    545 
    546   // The following defines the default order of apps.
    547 #if defined(OS_CHROMEOS)
    548   std::vector<std::string> app_ids;
    549   chromeos::default_app_order::Get(&app_ids);
    550 #else
    551   const char* kDefaultAppOrder[] = {
    552     extension_misc::kChromeAppId,
    553     extension_misc::kWebStoreAppId,
    554   };
    555   const std::vector<const char*> app_ids(
    556       kDefaultAppOrder, kDefaultAppOrder + arraysize(kDefaultAppOrder));
    557 #endif
    558 
    559   syncer::StringOrdinal page_ordinal = CreateFirstAppPageOrdinal();
    560   syncer::StringOrdinal app_launch_ordinal =
    561       CreateFirstAppLaunchOrdinal(page_ordinal);
    562   for (size_t i = 0; i < app_ids.size(); ++i) {
    563     const std::string extension_id = app_ids[i];
    564     default_ordinals_[extension_id].page_ordinal = page_ordinal;
    565     default_ordinals_[extension_id].app_launch_ordinal = app_launch_ordinal;
    566     app_launch_ordinal = app_launch_ordinal.CreateAfter();
    567   }
    568 }
    569 
    570 bool ExtensionSorting::GetDefaultOrdinals(
    571     const std::string& extension_id,
    572     syncer::StringOrdinal* page_ordinal,
    573     syncer::StringOrdinal* app_launch_ordinal) {
    574   CreateDefaultOrdinals();
    575   AppOrdinalsMap::const_iterator it = default_ordinals_.find(extension_id);
    576   if (it == default_ordinals_.end())
    577     return false;
    578 
    579   if (page_ordinal)
    580     *page_ordinal = it->second.page_ordinal;
    581   if (app_launch_ordinal)
    582     *app_launch_ordinal = it->second.app_launch_ordinal;
    583   return true;
    584 }
    585 
    586 syncer::StringOrdinal ExtensionSorting::ResolveCollision(
    587     const syncer::StringOrdinal& page_ordinal,
    588     const syncer::StringOrdinal& app_launch_ordinal) const {
    589   DCHECK(page_ordinal.IsValid() && app_launch_ordinal.IsValid());
    590 
    591   PageOrdinalMap::const_iterator page_it = ntp_ordinal_map_.find(page_ordinal);
    592   if (page_it == ntp_ordinal_map_.end())
    593     return app_launch_ordinal;
    594 
    595   const AppLaunchOrdinalMap& page = page_it->second;
    596   AppLaunchOrdinalMap::const_iterator app_it = page.find(app_launch_ordinal);
    597   if (app_it == page.end())
    598     return app_launch_ordinal;
    599 
    600   // Finds the next app launcher ordinal. This is done by the following loop
    601   // because this function could be called before FixNTPOrdinalCollisions and
    602   // thus |page| might contains multiple entries with the same app launch
    603   // ordinal. See http://crbug.com/155603
    604   while (app_it != page.end() && app_launch_ordinal.Equals(app_it->first))
    605     ++app_it;
    606 
    607   // If there is no next after the collision, returns the next ordinal.
    608   if (app_it == page.end())
    609     return app_launch_ordinal.CreateAfter();
    610 
    611   // Otherwise, returns the ordinal between the collision and the next ordinal.
    612   return app_launch_ordinal.CreateBetween(app_it->first);
    613 }
    614 
    615 size_t ExtensionSorting::CountItemsVisibleOnNtp(
    616     const AppLaunchOrdinalMap& m) const {
    617   size_t result = 0;
    618   for (AppLaunchOrdinalMap::const_iterator it = m.begin(); it != m.end();
    619        ++it) {
    620     const std::string& id = it->second;
    621     if (ntp_hidden_extensions_.count(id) == 0)
    622       result++;
    623   }
    624   return result;
    625 }
    626