diff --git a/storage/SQLCollations.cpp b/storage/SQLCollations.cpp index 1495ecc1da17..3cbeafe5d0be 100644 --- a/storage/SQLCollations.cpp +++ b/storage/SQLCollations.cpp @@ -5,9 +5,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ArrayUtils.h" +#include "mozilla/intl/Collator.h" #include "SQLCollations.h" +using mozilla::intl::Collator; + namespace mozilla { namespace storage { @@ -20,7 +23,7 @@ namespace { * Helper function for the UTF-8 locale collations. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes in aStr1. * @param aStr1 @@ -31,26 +34,26 @@ namespace { * @param aStr2 * The string to be compared against aStr1 as provided by SQLite. It * must be a non-null-terminated char* buffer. - * @param aComparisonStrength - * The sorting strength, one of the nsICollation constants. + * @param aSensitivity + * The sorting sensitivity. * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number. * If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2, * returns 0. */ int localeCollationHelper8(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2, - int32_t aComparisonStrength) { + Collator::Sensitivity aSensitivity) { NS_ConvertUTF8toUTF16 str1(static_cast(aStr1), aLen1); NS_ConvertUTF8toUTF16 str2(static_cast(aStr2), aLen2); Service* serv = static_cast(aService); - return serv->localeCompareStrings(str1, str2, aComparisonStrength); + return serv->localeCompareStrings(str1, str2, aSensitivity); } /** * Helper function for the UTF-16 locale collations. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes (not characters) in aStr1. * @param aStr1 @@ -61,15 +64,15 @@ int localeCollationHelper8(void* aService, int aLen1, const void* aStr1, * @param aStr2 * The string to be compared against aStr1 as provided by SQLite. It * must be a non-null-terminated char16_t* buffer. - * @param aComparisonStrength - * The sorting strength, one of the nsICollation constants. + * @param aSensitivity + * The sorting sensitivity. * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number. * If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2, * returns 0. */ int localeCollationHelper16(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2, - int32_t aComparisonStrength) { + Collator::Sensitivity aSensitivity) { const char16_t* buf1 = static_cast(aStr1); const char16_t* buf2 = static_cast(aStr2); @@ -80,7 +83,7 @@ int localeCollationHelper16(void* aService, int aLen1, const void* aStr1, nsDependentSubstring str1(buf1, buf1 + (aLen1 / sizeof(char16_t))); nsDependentSubstring str2(buf2, buf2 + (aLen2 / sizeof(char16_t))); Service* serv = static_cast(aService); - return serv->localeCompareStrings(str1, str2, aComparisonStrength); + return serv->localeCompareStrings(str1, str2, aSensitivity); } // This struct is used only by registerCollations below, but ISO C++98 forbids @@ -127,53 +130,53 @@ int registerCollations(sqlite3* aDB, Service* aService) { int localeCollation8(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseInSensitive); + Collator::Sensitivity::Base); } int localeCollationCaseSensitive8(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationAccentInsenstive); + Collator::Sensitivity::Case); } int localeCollationAccentSensitive8(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseInsensitiveAscii); + Collator::Sensitivity::Accent); } int localeCollationCaseAccentSensitive8(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseSensitive); + Collator::Sensitivity::Variant); } int localeCollation16(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseInSensitive); + Collator::Sensitivity::Base); } int localeCollationCaseSensitive16(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationAccentInsenstive); + Collator::Sensitivity::Case); } int localeCollationAccentSensitive16(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseInsensitiveAscii); + Collator::Sensitivity::Accent); } int localeCollationCaseAccentSensitive16(void* aService, int aLen1, const void* aStr1, int aLen2, const void* aStr2) { return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, - nsICollation::kCollationCaseSensitive); + Collator::Sensitivity::Variant); } } // namespace storage diff --git a/storage/SQLCollations.h b/storage/SQLCollations.h index d0bb53d2db11..184508462172 100644 --- a/storage/SQLCollations.h +++ b/storage/SQLCollations.h @@ -23,7 +23,7 @@ namespace storage { * @param aDB * The database we'll be registering the collations with. * @param aService - * The Service that owns the nsICollation used by our collations. + * The Service that owns the collator used by our collations. * @return the SQLite status code indicating success or failure. */ int registerCollations(sqlite3* aDB, Service* aService); @@ -36,7 +36,7 @@ int registerCollations(sqlite3* aDB, Service* aService); * Comparison is case- and accent-insensitive. This is called by SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes in aStr1. * @param aStr1 @@ -60,7 +60,7 @@ int localeCollation8(void* aService, int aLen1, const void* aStr1, int aLen2, * SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes in aStr1. * @param aStr1 @@ -84,7 +84,7 @@ int localeCollationCaseSensitive8(void* aService, int aLen1, const void* aStr1, * SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes in aStr1. * @param aStr1 @@ -108,7 +108,7 @@ int localeCollationAccentSensitive8(void* aService, int aLen1, * Comparison is case- and accent-sensitive. This is called by SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes in aStr1. * @param aStr1 @@ -132,7 +132,7 @@ int localeCollationCaseAccentSensitive8(void* aService, int aLen1, * Comparison is case- and accent-insensitive. This is called by SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes (not characters) in aStr1. * @param aStr1 @@ -156,7 +156,7 @@ int localeCollation16(void* aService, int aLen1, const void* aStr1, int aLen2, * SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes (not characters) in aStr1. * @param aStr1 @@ -180,7 +180,7 @@ int localeCollationCaseSensitive16(void* aService, int aLen1, const void* aStr1, * SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes (not characters) in aStr1. * @param aStr1 @@ -204,7 +204,7 @@ int localeCollationAccentSensitive16(void* aService, int aLen1, * Comparison is case- and accent-sensitive. This is called by SQLite. * * @param aService - * The Service that owns the nsICollation used by this collation. + * The Service that owns the collator used by this collation. * @param aLen1 * The number of bytes (not characters) in aStr1. * @param aStr1 diff --git a/storage/mozStorageService.cpp b/storage/mozStorageService.cpp index ca1a94d0f9e0..3ee752150bac 100644 --- a/storage/mozStorageService.cpp +++ b/storage/mozStorageService.cpp @@ -22,6 +22,8 @@ #include "mozIStorageCompletionCallback.h" #include "mozIStoragePendingStatement.h" #include "mozilla/StaticPrefs_storage.h" +#include "mozilla/intl/Collator.h" +#include "mozilla/intl/LocaleService.h" #include "sqlite3.h" #include "mozilla/AutoSQLiteLifetime.h" @@ -31,6 +33,8 @@ # undef CompareString #endif +using mozilla::intl::Collator; + namespace mozilla { namespace storage { @@ -354,47 +358,58 @@ nsresult Service::initialize() { int Service::localeCompareStrings(const nsAString& aStr1, const nsAString& aStr2, - int32_t aComparisonStrength) { - // The implementation of nsICollation.CompareString() is platform-dependent. - // On Linux it's not thread-safe. It may not be on Windows and OS X either, - // but it's more difficult to tell. We therefore synchronize this method. + Collator::Sensitivity aSensitivity) { + // The mozilla::intl::Collator is not thread safe, since the Collator::Options + // can be changed. MutexAutoLock mutex(mMutex); - nsICollation* coll = getLocaleCollation(); - if (!coll) { + Collator* collator = getCollator(); + if (!collator) { NS_ERROR("Storage service has no collation"); return 0; } - int32_t res; - nsresult rv = coll->CompareString(aComparisonStrength, aStr1, aStr2, &res); - if (NS_FAILED(rv)) { - NS_ERROR("Collation compare string failed"); - return 0; + if (aSensitivity != mLastSensitivity) { + auto result = + mCollator->SetOptions(Collator::Options{.sensitivity = aSensitivity}); + + if (result.isErr()) { + NS_WARNING("Could not configure the mozilla::intl::Collation."); + return 0; + } + mLastSensitivity = aSensitivity; } - return res; + return collator->CompareStrings(aStr1, aStr2); } -nsICollation* Service::getLocaleCollation() { +Collator* Service::getCollator() { mMutex.AssertCurrentThreadOwns(); - if (mLocaleCollation) return mLocaleCollation; + if (mCollator) { + return mCollator.get(); + } - nsCOMPtr collFact = - do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); - if (!collFact) { - NS_WARNING("Could not create collation factory"); + auto result = mozilla::intl::LocaleService::TryCreateComponent(); + if (result.isErr()) { + NS_WARNING("Could not create mozilla::intl::Collation."); return nullptr; } - nsresult rv = collFact->CreateCollation(getter_AddRefs(mLocaleCollation)); - if (NS_FAILED(rv)) { - NS_WARNING("Could not create collation"); + mCollator = result.unwrap(); + + // Sort in a case-insensitive way, where "base" letters are considered + // equal, e.g: a = á, a = A, a ≠ b. + auto optResult = mCollator->SetOptions( + Collator::Options{.sensitivity = Collator::Sensitivity::Base}); + + if (optResult.isErr()) { + NS_WARNING("Could not configure the mozilla::intl::Collation."); + mCollator = nullptr; return nullptr; } - return mLocaleCollation; + return mCollator.get(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/storage/mozStorageService.h b/storage/mozStorageService.h index 9b1e973f5281..3b40b07d41db 100644 --- a/storage/mozStorageService.h +++ b/storage/mozStorageService.h @@ -8,17 +8,21 @@ #define MOZSTORAGESERVICE_H #include "nsCOMPtr.h" -#include "nsICollation.h" #include "nsIFile.h" #include "nsIMemoryReporter.h" #include "nsIObserver.h" #include "nsTArray.h" #include "mozilla/Mutex.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/intl/Collator.h" #include "mozIStorageService.h" class nsIMemoryReporter; struct sqlite3_vfs; +namespace mozilla::intl { +class Collator; +} namespace mozilla { namespace storage { @@ -40,14 +44,14 @@ class Service : public mozIStorageService, * The string to be compared against aStr2. * @param aStr2 * The string to be compared against aStr1. - * @param aComparisonStrength - * The sorting strength, one of the nsICollation constants. + * @param aSensitivity + * The sorting sensitivity. * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative * number. If aStr1 > aStr2, returns a positive number. If * aStr1 == aStr2, returns 0. */ int localeCompareStrings(const nsAString& aStr1, const nsAString& aStr2, - int32_t aComparisonStrength); + mozilla::intl::Collator::Sensitivity aSensitivity); static already_AddRefed getSingleton(); @@ -147,28 +151,30 @@ class Service : public mozIStorageService, void minimizeMemory(); /** - * Lazily creates and returns a collation created from the application's + * Lazily creates and returns a collator created from the application's * locale that all statements of all Connections of this Service may use. - * Since the collation's lifetime is that of the Service and no statement may + * Since the collator's lifetime is that of the Service and no statement may * execute outside the lifetime of the Service, this method returns a raw * pointer. */ - nsICollation* getLocaleCollation(); + mozilla::intl::Collator* getCollator(); /** - * Lazily created collation that all statements of all Connections of this - * Service may use. The collation is created from the application's locale. + * Lazily created collator that all statements of all Connections of this + * Service may use. The collator is created from the application's locale. * - * @note Collation implementations are platform-dependent and in general not - * thread-safe. Access to this collation should be synchronized. + * @note The collator is not thread-safe since the options can be changed + * between calls. Access should be synchronized. */ - nsCOMPtr mLocaleCollation; + mozilla::UniquePtr mCollator = nullptr; nsCOMPtr mProfileStorageFile; nsCOMPtr mStorageSQLiteReporter; static Service* gService; + + mozilla::intl::Collator::Sensitivity mLastSensitivity; }; } // namespace storage