1 // Copyright 2013 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.
5 #include "chrome/browser/extensions/chrome_app_sorting.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/extension_scoped_prefs.h"
12 #include "chrome/browser/extensions/extension_sync_service.h"
13 #include "chrome/common/extensions/extension_constants.h"
14 #include "content/public/browser/notification_service.h"
15 #include "extensions/common/extension.h"
17 #if defined(OS_CHROMEOS)
18 #include "chrome/browser/chromeos/extensions/default_app_order.h"
21 namespace extensions
{
25 // The number of apps per page. This isn't a hard limit, but new apps installed
26 // from the webstore will overflow onto a new page if this limit is reached.
27 const size_t kNaturalAppPageSize
= 18;
29 // A preference determining the order of which the apps appear on the NTP.
30 const char kPrefAppLaunchIndexDeprecated
[] = "app_launcher_index";
31 const char kPrefAppLaunchOrdinal
[] = "app_launcher_ordinal";
33 // A preference determining the page on which an app appears in the NTP.
34 const char kPrefPageIndexDeprecated
[] = "page_index";
35 const char kPrefPageOrdinal
[] = "page_ordinal";
39 ////////////////////////////////////////////////////////////////////////////////
40 // ChromeAppSorting::AppOrdinals
42 ChromeAppSorting::AppOrdinals::AppOrdinals() {}
44 ChromeAppSorting::AppOrdinals::~AppOrdinals() {}
46 ////////////////////////////////////////////////////////////////////////////////
49 ChromeAppSorting::ChromeAppSorting()
50 : extension_scoped_prefs_(NULL
),
51 extension_sync_service_(NULL
),
52 default_ordinals_created_(false) {
55 ChromeAppSorting::~ChromeAppSorting() {
58 void ChromeAppSorting::SetExtensionScopedPrefs(ExtensionScopedPrefs
* prefs
) {
59 extension_scoped_prefs_
= prefs
;
62 void ChromeAppSorting::SetExtensionSyncService(
63 ExtensionSyncService
* extension_sync_service
) {
64 extension_sync_service_
= extension_sync_service
;
67 void ChromeAppSorting::Initialize(
68 const extensions::ExtensionIdList
& extension_ids
) {
69 InitializePageOrdinalMap(extension_ids
);
71 MigrateAppIndex(extension_ids
);
74 void ChromeAppSorting::CreateOrdinalsIfNecessary(size_t minimum_size
) {
75 // Create StringOrdinal values as required to ensure |ntp_ordinal_map_| has at
76 // least |minimum_size| entries.
77 if (ntp_ordinal_map_
.empty() && minimum_size
> 0)
78 ntp_ordinal_map_
[syncer::StringOrdinal::CreateInitialOrdinal()];
80 while (ntp_ordinal_map_
.size() < minimum_size
) {
81 syncer::StringOrdinal filler
=
82 ntp_ordinal_map_
.rbegin()->first
.CreateAfter();
83 AppLaunchOrdinalMap empty_ordinal_map
;
84 ntp_ordinal_map_
.insert(std::make_pair(filler
, empty_ordinal_map
));
88 void ChromeAppSorting::MigrateAppIndex(
89 const extensions::ExtensionIdList
& extension_ids
) {
90 if (extension_ids
.empty())
93 // Convert all the page index values to page ordinals. If there are any
94 // app launch values that need to be migrated, inserted them into a sorted
95 // set to be dealt with later.
96 typedef std::map
<syncer::StringOrdinal
, std::map
<int, const std::string
*>,
97 syncer::StringOrdinal::LessThanFn
> AppPositionToIdMapping
;
98 AppPositionToIdMapping app_launches_to_convert
;
99 for (extensions::ExtensionIdList::const_iterator ext_id
=
100 extension_ids
.begin(); ext_id
!= extension_ids
.end(); ++ext_id
) {
101 int old_page_index
= 0;
102 syncer::StringOrdinal page
= GetPageOrdinal(*ext_id
);
103 if (extension_scoped_prefs_
->ReadPrefAsInteger(
105 kPrefPageIndexDeprecated
,
107 // Some extensions have invalid page index, so we don't
108 // attempt to convert them.
109 if (old_page_index
< 0) {
110 DLOG(WARNING
) << "Extension " << *ext_id
111 << " has an invalid page index " << old_page_index
112 << ". Aborting attempt to convert its index.";
116 CreateOrdinalsIfNecessary(static_cast<size_t>(old_page_index
) + 1);
118 page
= PageIntegerAsStringOrdinal(old_page_index
);
119 SetPageOrdinal(*ext_id
, page
);
120 extension_scoped_prefs_
->UpdateExtensionPref(
121 *ext_id
, kPrefPageIndexDeprecated
, NULL
);
124 int old_app_launch_index
= 0;
125 if (extension_scoped_prefs_
->ReadPrefAsInteger(
127 kPrefAppLaunchIndexDeprecated
,
128 &old_app_launch_index
)) {
129 // We can't update the app launch index value yet, because we use
130 // GetNextAppLaunchOrdinal to get the new ordinal value and it requires
131 // all the ordinals with lower values to have already been migrated.
132 // A valid page ordinal is also required because otherwise there is
133 // no page to add the app to.
135 app_launches_to_convert
[page
][old_app_launch_index
] = &*ext_id
;
137 extension_scoped_prefs_
->UpdateExtensionPref(
138 *ext_id
, kPrefAppLaunchIndexDeprecated
, NULL
);
142 // Remove any empty pages that may have been added. This shouldn't occur,
143 // but double check here to prevent future problems with conversions between
144 // integers and StringOrdinals.
145 for (PageOrdinalMap::iterator it
= ntp_ordinal_map_
.begin();
146 it
!= ntp_ordinal_map_
.end();) {
147 if (it
->second
.empty()) {
148 PageOrdinalMap::iterator prev_it
= it
;
150 ntp_ordinal_map_
.erase(prev_it
);
156 if (app_launches_to_convert
.empty())
159 // Create the new app launch ordinals and remove the old preferences. Since
160 // the set is sorted, each time we migrate an apps index, we know that all of
161 // the remaining apps will appear further down the NTP than it or on a
163 for (AppPositionToIdMapping::const_iterator page_it
=
164 app_launches_to_convert
.begin();
165 page_it
!= app_launches_to_convert
.end(); ++page_it
) {
166 syncer::StringOrdinal page
= page_it
->first
;
167 for (std::map
<int, const std::string
*>::const_iterator launch_it
=
168 page_it
->second
.begin(); launch_it
!= page_it
->second
.end();
170 SetAppLaunchOrdinal(*(launch_it
->second
),
171 CreateNextAppLaunchOrdinal(page
));
176 void ChromeAppSorting::FixNTPOrdinalCollisions() {
177 for (PageOrdinalMap::iterator page_it
= ntp_ordinal_map_
.begin();
178 page_it
!= ntp_ordinal_map_
.end(); ++page_it
) {
179 AppLaunchOrdinalMap
& page
= page_it
->second
;
181 AppLaunchOrdinalMap::iterator app_launch_it
= page
.begin();
182 while (app_launch_it
!= page
.end()) {
183 int app_count
= page
.count(app_launch_it
->first
);
184 if (app_count
== 1) {
189 syncer::StringOrdinal repeated_ordinal
= app_launch_it
->first
;
191 // Sort the conflicting keys by their extension id, this is how
192 // the order is decided.
193 std::vector
<std::string
> conflicting_ids
;
194 for (int i
= 0; i
< app_count
; ++i
, ++app_launch_it
)
195 conflicting_ids
.push_back(app_launch_it
->second
);
196 std::sort(conflicting_ids
.begin(), conflicting_ids
.end());
198 syncer::StringOrdinal upper_bound_ordinal
= app_launch_it
== page
.end() ?
199 syncer::StringOrdinal() :
200 app_launch_it
->first
;
201 syncer::StringOrdinal lower_bound_ordinal
= repeated_ordinal
;
203 // Start at position 1 because the first extension can keep the conflicted
205 for (int i
= 1; i
< app_count
; ++i
) {
206 syncer::StringOrdinal unique_app_launch
;
207 if (upper_bound_ordinal
.IsValid()) {
209 lower_bound_ordinal
.CreateBetween(upper_bound_ordinal
);
211 unique_app_launch
= lower_bound_ordinal
.CreateAfter();
214 SetAppLaunchOrdinal(conflicting_ids
[i
], unique_app_launch
);
215 lower_bound_ordinal
= unique_app_launch
;
220 content::NotificationService::current()->Notify(
221 chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED
,
222 content::Source
<ChromeAppSorting
>(this),
223 content::NotificationService::NoDetails());
226 void ChromeAppSorting::EnsureValidOrdinals(
227 const std::string
& extension_id
,
228 const syncer::StringOrdinal
& suggested_page
) {
229 syncer::StringOrdinal page_ordinal
= GetPageOrdinal(extension_id
);
230 if (!page_ordinal
.IsValid()) {
231 if (suggested_page
.IsValid()) {
232 page_ordinal
= suggested_page
;
233 } else if (!GetDefaultOrdinals(extension_id
, &page_ordinal
, NULL
) ||
234 !page_ordinal
.IsValid()) {
235 page_ordinal
= GetNaturalAppPageOrdinal();
238 SetPageOrdinal(extension_id
, page_ordinal
);
241 syncer::StringOrdinal app_launch_ordinal
= GetAppLaunchOrdinal(extension_id
);
242 if (!app_launch_ordinal
.IsValid()) {
243 // If using default app launcher ordinal, make sure there is no collision.
244 if (GetDefaultOrdinals(extension_id
, NULL
, &app_launch_ordinal
) &&
245 app_launch_ordinal
.IsValid())
246 app_launch_ordinal
= ResolveCollision(page_ordinal
, app_launch_ordinal
);
248 app_launch_ordinal
= CreateNextAppLaunchOrdinal(page_ordinal
);
250 SetAppLaunchOrdinal(extension_id
, app_launch_ordinal
);
254 void ChromeAppSorting::OnExtensionMoved(
255 const std::string
& moved_extension_id
,
256 const std::string
& predecessor_extension_id
,
257 const std::string
& successor_extension_id
) {
258 // We only need to change the StringOrdinal if there are neighbours.
259 if (!predecessor_extension_id
.empty() || !successor_extension_id
.empty()) {
260 if (predecessor_extension_id
.empty()) {
264 GetAppLaunchOrdinal(successor_extension_id
).CreateBefore());
265 } else if (successor_extension_id
.empty()) {
266 // Only a predecessor.
269 GetAppLaunchOrdinal(predecessor_extension_id
).CreateAfter());
271 // Both a successor and predecessor
272 const syncer::StringOrdinal
& predecessor_ordinal
=
273 GetAppLaunchOrdinal(predecessor_extension_id
);
274 const syncer::StringOrdinal
& successor_ordinal
=
275 GetAppLaunchOrdinal(successor_extension_id
);
276 SetAppLaunchOrdinal(moved_extension_id
,
277 predecessor_ordinal
.CreateBetween(successor_ordinal
));
281 content::NotificationService::current()->Notify(
282 chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED
,
283 content::Source
<ChromeAppSorting
>(this),
284 content::Details
<const std::string
>(&moved_extension_id
));
288 syncer::StringOrdinal
ChromeAppSorting::GetAppLaunchOrdinal(
289 const std::string
& extension_id
) const {
290 std::string raw_value
;
291 // If the preference read fails then raw_value will still be unset and we
292 // will return an invalid StringOrdinal to signal that no app launch ordinal
294 extension_scoped_prefs_
->ReadPrefAsString(
295 extension_id
, kPrefAppLaunchOrdinal
, &raw_value
);
296 return syncer::StringOrdinal(raw_value
);
299 void ChromeAppSorting::SetAppLaunchOrdinal(
300 const std::string
& extension_id
,
301 const syncer::StringOrdinal
& new_app_launch_ordinal
) {
302 // No work is required if the old and new values are the same.
303 if (new_app_launch_ordinal
.EqualsOrBothInvalid(
304 GetAppLaunchOrdinal(extension_id
))) {
308 syncer::StringOrdinal page_ordinal
= GetPageOrdinal(extension_id
);
309 RemoveOrdinalMapping(
310 extension_id
, page_ordinal
, GetAppLaunchOrdinal(extension_id
));
311 AddOrdinalMapping(extension_id
, page_ordinal
, new_app_launch_ordinal
);
313 base::Value
* new_value
= new_app_launch_ordinal
.IsValid() ?
314 new base::StringValue(new_app_launch_ordinal
.ToInternalValue()) :
317 extension_scoped_prefs_
->UpdateExtensionPref(
319 kPrefAppLaunchOrdinal
,
321 SyncIfNeeded(extension_id
);
324 syncer::StringOrdinal
ChromeAppSorting::CreateFirstAppLaunchOrdinal(
325 const syncer::StringOrdinal
& page_ordinal
) const {
326 const syncer::StringOrdinal
& min_ordinal
=
327 GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal
,
328 ChromeAppSorting::MIN_ORDINAL
);
330 if (min_ordinal
.IsValid())
331 return min_ordinal
.CreateBefore();
333 return syncer::StringOrdinal::CreateInitialOrdinal();
336 syncer::StringOrdinal
ChromeAppSorting::CreateNextAppLaunchOrdinal(
337 const syncer::StringOrdinal
& page_ordinal
) const {
338 const syncer::StringOrdinal
& max_ordinal
=
339 GetMinOrMaxAppLaunchOrdinalsOnPage(page_ordinal
,
340 ChromeAppSorting::MAX_ORDINAL
);
342 if (max_ordinal
.IsValid())
343 return max_ordinal
.CreateAfter();
345 return syncer::StringOrdinal::CreateInitialOrdinal();
348 syncer::StringOrdinal
ChromeAppSorting::CreateFirstAppPageOrdinal() const {
349 if (ntp_ordinal_map_
.empty())
350 return syncer::StringOrdinal::CreateInitialOrdinal();
352 return ntp_ordinal_map_
.begin()->first
;
355 syncer::StringOrdinal
ChromeAppSorting::GetNaturalAppPageOrdinal() const {
356 if (ntp_ordinal_map_
.empty())
357 return syncer::StringOrdinal::CreateInitialOrdinal();
359 for (PageOrdinalMap::const_iterator it
= ntp_ordinal_map_
.begin();
360 it
!= ntp_ordinal_map_
.end(); ++it
) {
361 if (CountItemsVisibleOnNtp(it
->second
) < kNaturalAppPageSize
)
365 // Add a new page as all existing pages are full.
366 syncer::StringOrdinal last_element
= ntp_ordinal_map_
.rbegin()->first
;
367 return last_element
.CreateAfter();
370 syncer::StringOrdinal
ChromeAppSorting::GetPageOrdinal(
371 const std::string
& extension_id
) const {
372 std::string raw_data
;
373 // If the preference read fails then raw_data will still be unset and we will
374 // return an invalid StringOrdinal to signal that no page ordinal was found.
375 extension_scoped_prefs_
->ReadPrefAsString(
376 extension_id
, kPrefPageOrdinal
, &raw_data
);
377 return syncer::StringOrdinal(raw_data
);
380 void ChromeAppSorting::SetPageOrdinal(
381 const std::string
& extension_id
,
382 const syncer::StringOrdinal
& new_page_ordinal
) {
383 // No work is required if the old and new values are the same.
384 if (new_page_ordinal
.EqualsOrBothInvalid(GetPageOrdinal(extension_id
)))
387 syncer::StringOrdinal app_launch_ordinal
= GetAppLaunchOrdinal(extension_id
);
388 RemoveOrdinalMapping(
389 extension_id
, GetPageOrdinal(extension_id
), app_launch_ordinal
);
390 AddOrdinalMapping(extension_id
, new_page_ordinal
, app_launch_ordinal
);
392 base::Value
* new_value
= new_page_ordinal
.IsValid() ?
393 new base::StringValue(new_page_ordinal
.ToInternalValue()) :
396 extension_scoped_prefs_
->UpdateExtensionPref(
400 SyncIfNeeded(extension_id
);
403 void ChromeAppSorting::ClearOrdinals(const std::string
& extension_id
) {
404 RemoveOrdinalMapping(extension_id
,
405 GetPageOrdinal(extension_id
),
406 GetAppLaunchOrdinal(extension_id
));
408 extension_scoped_prefs_
->UpdateExtensionPref(
409 extension_id
, kPrefPageOrdinal
, NULL
);
410 extension_scoped_prefs_
->UpdateExtensionPref(
411 extension_id
, kPrefAppLaunchOrdinal
, NULL
);
414 int ChromeAppSorting::PageStringOrdinalAsInteger(
415 const syncer::StringOrdinal
& page_ordinal
) const {
416 if (!page_ordinal
.IsValid())
419 PageOrdinalMap::const_iterator it
= ntp_ordinal_map_
.find(page_ordinal
);
420 return it
!= ntp_ordinal_map_
.end() ?
421 std::distance(ntp_ordinal_map_
.begin(), it
) : -1;
424 syncer::StringOrdinal
ChromeAppSorting::PageIntegerAsStringOrdinal(
426 if (page_index
< ntp_ordinal_map_
.size()) {
427 PageOrdinalMap::const_iterator it
= ntp_ordinal_map_
.begin();
428 std::advance(it
, page_index
);
432 CreateOrdinalsIfNecessary(page_index
+ 1);
433 return ntp_ordinal_map_
.rbegin()->first
;
436 void ChromeAppSorting::MarkExtensionAsHidden(const std::string
& extension_id
) {
437 ntp_hidden_extensions_
.insert(extension_id
);
440 syncer::StringOrdinal
ChromeAppSorting::GetMinOrMaxAppLaunchOrdinalsOnPage(
441 const syncer::StringOrdinal
& target_page_ordinal
,
442 AppLaunchOrdinalReturn return_type
) const {
443 CHECK(target_page_ordinal
.IsValid());
445 syncer::StringOrdinal return_value
;
447 PageOrdinalMap::const_iterator page
=
448 ntp_ordinal_map_
.find(target_page_ordinal
);
449 if (page
!= ntp_ordinal_map_
.end()) {
450 const AppLaunchOrdinalMap
& app_list
= page
->second
;
452 if (app_list
.empty())
453 return syncer::StringOrdinal();
455 if (return_type
== ChromeAppSorting::MAX_ORDINAL
)
456 return_value
= app_list
.rbegin()->first
;
457 else if (return_type
== ChromeAppSorting::MIN_ORDINAL
)
458 return_value
= app_list
.begin()->first
;
464 void ChromeAppSorting::InitializePageOrdinalMap(
465 const extensions::ExtensionIdList
& extension_ids
) {
466 for (extensions::ExtensionIdList::const_iterator ext_it
=
467 extension_ids
.begin(); ext_it
!= extension_ids
.end(); ++ext_it
) {
468 AddOrdinalMapping(*ext_it
,
469 GetPageOrdinal(*ext_it
),
470 GetAppLaunchOrdinal(*ext_it
));
472 // Ensure that the web store app still isn't found in this list, since
473 // it is added after this loop.
474 DCHECK(*ext_it
!= extension_misc::kWebStoreAppId
);
475 DCHECK(*ext_it
!= extension_misc::kChromeAppId
);
478 // Include the Web Store App since it is displayed on the NTP.
479 syncer::StringOrdinal web_store_app_page
=
480 GetPageOrdinal(extension_misc::kWebStoreAppId
);
481 if (web_store_app_page
.IsValid()) {
482 AddOrdinalMapping(extension_misc::kWebStoreAppId
,
484 GetAppLaunchOrdinal(extension_misc::kWebStoreAppId
));
486 // Include the Chrome App since it is displayed in the app launcher.
487 syncer::StringOrdinal chrome_app_page
=
488 GetPageOrdinal(extension_misc::kChromeAppId
);
489 if (chrome_app_page
.IsValid()) {
490 AddOrdinalMapping(extension_misc::kChromeAppId
,
492 GetAppLaunchOrdinal(extension_misc::kChromeAppId
));
496 void ChromeAppSorting::AddOrdinalMapping(
497 const std::string
& extension_id
,
498 const syncer::StringOrdinal
& page_ordinal
,
499 const syncer::StringOrdinal
& app_launch_ordinal
) {
500 if (!page_ordinal
.IsValid() || !app_launch_ordinal
.IsValid())
503 ntp_ordinal_map_
[page_ordinal
].insert(
504 std::make_pair(app_launch_ordinal
, extension_id
));
507 void ChromeAppSorting::RemoveOrdinalMapping(
508 const std::string
& extension_id
,
509 const syncer::StringOrdinal
& page_ordinal
,
510 const syncer::StringOrdinal
& app_launch_ordinal
) {
511 if (!page_ordinal
.IsValid() || !app_launch_ordinal
.IsValid())
514 // Check that the page exists using find to prevent creating a new page
515 // if |page_ordinal| isn't a used page.
516 PageOrdinalMap::iterator page_map
= ntp_ordinal_map_
.find(page_ordinal
);
517 if (page_map
== ntp_ordinal_map_
.end())
520 for (AppLaunchOrdinalMap::iterator it
=
521 page_map
->second
.find(app_launch_ordinal
);
522 it
!= page_map
->second
.end(); ++it
) {
523 if (it
->second
== extension_id
) {
524 page_map
->second
.erase(it
);
530 void ChromeAppSorting::SyncIfNeeded(const std::string
& extension_id
) {
531 if (extension_sync_service_
)
532 extension_sync_service_
->SyncOrderingChange(extension_id
);
535 void ChromeAppSorting::CreateDefaultOrdinals() {
536 if (default_ordinals_created_
)
538 default_ordinals_created_
= true;
540 // The following defines the default order of apps.
541 #if defined(OS_CHROMEOS)
542 std::vector
<std::string
> app_ids
;
543 chromeos::default_app_order::Get(&app_ids
);
545 const char* kDefaultAppOrder
[] = {
546 extension_misc::kChromeAppId
,
547 extension_misc::kWebStoreAppId
,
549 const std::vector
<const char*> app_ids(
550 kDefaultAppOrder
, kDefaultAppOrder
+ arraysize(kDefaultAppOrder
));
553 syncer::StringOrdinal page_ordinal
= CreateFirstAppPageOrdinal();
554 syncer::StringOrdinal app_launch_ordinal
=
555 CreateFirstAppLaunchOrdinal(page_ordinal
);
556 for (size_t i
= 0; i
< app_ids
.size(); ++i
) {
557 const std::string extension_id
= app_ids
[i
];
558 default_ordinals_
[extension_id
].page_ordinal
= page_ordinal
;
559 default_ordinals_
[extension_id
].app_launch_ordinal
= app_launch_ordinal
;
560 app_launch_ordinal
= app_launch_ordinal
.CreateAfter();
564 bool ChromeAppSorting::GetDefaultOrdinals(
565 const std::string
& extension_id
,
566 syncer::StringOrdinal
* page_ordinal
,
567 syncer::StringOrdinal
* app_launch_ordinal
) {
568 CreateDefaultOrdinals();
569 AppOrdinalsMap::const_iterator it
= default_ordinals_
.find(extension_id
);
570 if (it
== default_ordinals_
.end())
574 *page_ordinal
= it
->second
.page_ordinal
;
575 if (app_launch_ordinal
)
576 *app_launch_ordinal
= it
->second
.app_launch_ordinal
;
580 syncer::StringOrdinal
ChromeAppSorting::ResolveCollision(
581 const syncer::StringOrdinal
& page_ordinal
,
582 const syncer::StringOrdinal
& app_launch_ordinal
) const {
583 DCHECK(page_ordinal
.IsValid() && app_launch_ordinal
.IsValid());
585 PageOrdinalMap::const_iterator page_it
= ntp_ordinal_map_
.find(page_ordinal
);
586 if (page_it
== ntp_ordinal_map_
.end())
587 return app_launch_ordinal
;
589 const AppLaunchOrdinalMap
& page
= page_it
->second
;
590 AppLaunchOrdinalMap::const_iterator app_it
= page
.find(app_launch_ordinal
);
591 if (app_it
== page
.end())
592 return app_launch_ordinal
;
594 // Finds the next app launcher ordinal. This is done by the following loop
595 // because this function could be called before FixNTPOrdinalCollisions and
596 // thus |page| might contains multiple entries with the same app launch
597 // ordinal. See http://crbug.com/155603
598 while (app_it
!= page
.end() && app_launch_ordinal
.Equals(app_it
->first
))
601 // If there is no next after the collision, returns the next ordinal.
602 if (app_it
== page
.end())
603 return app_launch_ordinal
.CreateAfter();
605 // Otherwise, returns the ordinal between the collision and the next ordinal.
606 return app_launch_ordinal
.CreateBetween(app_it
->first
);
609 size_t ChromeAppSorting::CountItemsVisibleOnNtp(
610 const AppLaunchOrdinalMap
& m
) const {
612 for (AppLaunchOrdinalMap::const_iterator it
= m
.begin(); it
!= m
.end();
614 const std::string
& id
= it
->second
;
615 if (ntp_hidden_extensions_
.count(id
) == 0)
621 } // namespace extensions