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