Bug 1719550 - Unify collator in mozStorageService and SQLCollations; r=platform-i18n-reviewers,nordzilla

Differential Revision: https://phabricator.services.mozilla.com/D121432
This commit is contained in:
Greg Tatum 2021-08-10 16:01:18 +00:00
parent ae55a94309
commit 2684357ea1
4 changed files with 87 additions and 61 deletions

View file

@ -5,9 +5,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/intl/Collator.h"
#include "SQLCollations.h" #include "SQLCollations.h"
using mozilla::intl::Collator;
namespace mozilla { namespace mozilla {
namespace storage { namespace storage {
@ -20,7 +23,7 @@ namespace {
* Helper function for the UTF-8 locale collations. * Helper function for the UTF-8 locale collations.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes in aStr1. * The number of bytes in aStr1.
* @param aStr1 * @param aStr1
@ -31,26 +34,26 @@ namespace {
* @param aStr2 * @param aStr2
* The string to be compared against aStr1 as provided by SQLite. It * The string to be compared against aStr1 as provided by SQLite. It
* must be a non-null-terminated char* buffer. * must be a non-null-terminated char* buffer.
* @param aComparisonStrength * @param aSensitivity
* The sorting strength, one of the nsICollation constants. * The sorting sensitivity.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number. * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2, * If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0. * returns 0.
*/ */
int localeCollationHelper8(void* aService, int aLen1, const void* aStr1, int localeCollationHelper8(void* aService, int aLen1, const void* aStr1,
int aLen2, const void* aStr2, int aLen2, const void* aStr2,
int32_t aComparisonStrength) { Collator::Sensitivity aSensitivity) {
NS_ConvertUTF8toUTF16 str1(static_cast<const char*>(aStr1), aLen1); NS_ConvertUTF8toUTF16 str1(static_cast<const char*>(aStr1), aLen1);
NS_ConvertUTF8toUTF16 str2(static_cast<const char*>(aStr2), aLen2); NS_ConvertUTF8toUTF16 str2(static_cast<const char*>(aStr2), aLen2);
Service* serv = static_cast<Service*>(aService); Service* serv = static_cast<Service*>(aService);
return serv->localeCompareStrings(str1, str2, aComparisonStrength); return serv->localeCompareStrings(str1, str2, aSensitivity);
} }
/** /**
* Helper function for the UTF-16 locale collations. * Helper function for the UTF-16 locale collations.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes (not characters) in aStr1. * The number of bytes (not characters) in aStr1.
* @param aStr1 * @param aStr1
@ -61,15 +64,15 @@ int localeCollationHelper8(void* aService, int aLen1, const void* aStr1,
* @param aStr2 * @param aStr2
* The string to be compared against aStr1 as provided by SQLite. It * The string to be compared against aStr1 as provided by SQLite. It
* must be a non-null-terminated char16_t* buffer. * must be a non-null-terminated char16_t* buffer.
* @param aComparisonStrength * @param aSensitivity
* The sorting strength, one of the nsICollation constants. * The sorting sensitivity.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number. * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative number.
* If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2, * If aStr1 > aStr2, returns a positive number. If aStr1 == aStr2,
* returns 0. * returns 0.
*/ */
int localeCollationHelper16(void* aService, int aLen1, const void* aStr1, int localeCollationHelper16(void* aService, int aLen1, const void* aStr1,
int aLen2, const void* aStr2, int aLen2, const void* aStr2,
int32_t aComparisonStrength) { Collator::Sensitivity aSensitivity) {
const char16_t* buf1 = static_cast<const char16_t*>(aStr1); const char16_t* buf1 = static_cast<const char16_t*>(aStr1);
const char16_t* buf2 = static_cast<const char16_t*>(aStr2); const char16_t* buf2 = static_cast<const char16_t*>(aStr2);
@ -80,7 +83,7 @@ int localeCollationHelper16(void* aService, int aLen1, const void* aStr1,
nsDependentSubstring str1(buf1, buf1 + (aLen1 / sizeof(char16_t))); nsDependentSubstring str1(buf1, buf1 + (aLen1 / sizeof(char16_t)));
nsDependentSubstring str2(buf2, buf2 + (aLen2 / sizeof(char16_t))); nsDependentSubstring str2(buf2, buf2 + (aLen2 / sizeof(char16_t)));
Service* serv = static_cast<Service*>(aService); Service* serv = static_cast<Service*>(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 // 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, int localeCollation8(void* aService, int aLen1, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInSensitive); Collator::Sensitivity::Base);
} }
int localeCollationCaseSensitive8(void* aService, int aLen1, const void* aStr1, int localeCollationCaseSensitive8(void* aService, int aLen1, const void* aStr1,
int aLen2, const void* aStr2) { int aLen2, const void* aStr2) {
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationAccentInsenstive); Collator::Sensitivity::Case);
} }
int localeCollationAccentSensitive8(void* aService, int aLen1, int localeCollationAccentSensitive8(void* aService, int aLen1,
const void* aStr1, int aLen2, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInsensitiveAscii); Collator::Sensitivity::Accent);
} }
int localeCollationCaseAccentSensitive8(void* aService, int aLen1, int localeCollationCaseAccentSensitive8(void* aService, int aLen1,
const void* aStr1, int aLen2, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper8(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseSensitive); Collator::Sensitivity::Variant);
} }
int localeCollation16(void* aService, int aLen1, const void* aStr1, int aLen2, int localeCollation16(void* aService, int aLen1, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInSensitive); Collator::Sensitivity::Base);
} }
int localeCollationCaseSensitive16(void* aService, int aLen1, const void* aStr1, int localeCollationCaseSensitive16(void* aService, int aLen1, const void* aStr1,
int aLen2, const void* aStr2) { int aLen2, const void* aStr2) {
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationAccentInsenstive); Collator::Sensitivity::Case);
} }
int localeCollationAccentSensitive16(void* aService, int aLen1, int localeCollationAccentSensitive16(void* aService, int aLen1,
const void* aStr1, int aLen2, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseInsensitiveAscii); Collator::Sensitivity::Accent);
} }
int localeCollationCaseAccentSensitive16(void* aService, int aLen1, int localeCollationCaseAccentSensitive16(void* aService, int aLen1,
const void* aStr1, int aLen2, const void* aStr1, int aLen2,
const void* aStr2) { const void* aStr2) {
return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2, return localeCollationHelper16(aService, aLen1, aStr1, aLen2, aStr2,
nsICollation::kCollationCaseSensitive); Collator::Sensitivity::Variant);
} }
} // namespace storage } // namespace storage

View file

@ -23,7 +23,7 @@ namespace storage {
* @param aDB * @param aDB
* The database we'll be registering the collations with. * The database we'll be registering the collations with.
* @param aService * @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. * @return the SQLite status code indicating success or failure.
*/ */
int registerCollations(sqlite3* aDB, Service* aService); 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. * Comparison is case- and accent-insensitive. This is called by SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes in aStr1. * The number of bytes in aStr1.
* @param aStr1 * @param aStr1
@ -60,7 +60,7 @@ int localeCollation8(void* aService, int aLen1, const void* aStr1, int aLen2,
* SQLite. * SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes in aStr1. * The number of bytes in aStr1.
* @param aStr1 * @param aStr1
@ -84,7 +84,7 @@ int localeCollationCaseSensitive8(void* aService, int aLen1, const void* aStr1,
* SQLite. * SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes in aStr1. * The number of bytes in aStr1.
* @param aStr1 * @param aStr1
@ -108,7 +108,7 @@ int localeCollationAccentSensitive8(void* aService, int aLen1,
* Comparison is case- and accent-sensitive. This is called by SQLite. * Comparison is case- and accent-sensitive. This is called by SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes in aStr1. * The number of bytes in aStr1.
* @param aStr1 * @param aStr1
@ -132,7 +132,7 @@ int localeCollationCaseAccentSensitive8(void* aService, int aLen1,
* Comparison is case- and accent-insensitive. This is called by SQLite. * Comparison is case- and accent-insensitive. This is called by SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes (not characters) in aStr1. * The number of bytes (not characters) in aStr1.
* @param aStr1 * @param aStr1
@ -156,7 +156,7 @@ int localeCollation16(void* aService, int aLen1, const void* aStr1, int aLen2,
* SQLite. * SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes (not characters) in aStr1. * The number of bytes (not characters) in aStr1.
* @param aStr1 * @param aStr1
@ -180,7 +180,7 @@ int localeCollationCaseSensitive16(void* aService, int aLen1, const void* aStr1,
* SQLite. * SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes (not characters) in aStr1. * The number of bytes (not characters) in aStr1.
* @param aStr1 * @param aStr1
@ -204,7 +204,7 @@ int localeCollationAccentSensitive16(void* aService, int aLen1,
* Comparison is case- and accent-sensitive. This is called by SQLite. * Comparison is case- and accent-sensitive. This is called by SQLite.
* *
* @param aService * @param aService
* The Service that owns the nsICollation used by this collation. * The Service that owns the collator used by this collation.
* @param aLen1 * @param aLen1
* The number of bytes (not characters) in aStr1. * The number of bytes (not characters) in aStr1.
* @param aStr1 * @param aStr1

View file

@ -22,6 +22,8 @@
#include "mozIStorageCompletionCallback.h" #include "mozIStorageCompletionCallback.h"
#include "mozIStoragePendingStatement.h" #include "mozIStoragePendingStatement.h"
#include "mozilla/StaticPrefs_storage.h" #include "mozilla/StaticPrefs_storage.h"
#include "mozilla/intl/Collator.h"
#include "mozilla/intl/LocaleService.h"
#include "sqlite3.h" #include "sqlite3.h"
#include "mozilla/AutoSQLiteLifetime.h" #include "mozilla/AutoSQLiteLifetime.h"
@ -31,6 +33,8 @@
# undef CompareString # undef CompareString
#endif #endif
using mozilla::intl::Collator;
namespace mozilla { namespace mozilla {
namespace storage { namespace storage {
@ -354,47 +358,60 @@ nsresult Service::initialize() {
int Service::localeCompareStrings(const nsAString& aStr1, int Service::localeCompareStrings(const nsAString& aStr1,
const nsAString& aStr2, const nsAString& aStr2,
int32_t aComparisonStrength) { Collator::Sensitivity aSensitivity) {
// The implementation of nsICollation.CompareString() is platform-dependent. // The mozilla::intl::Collator is not thread safe, since the Collator::Options
// On Linux it's not thread-safe. It may not be on Windows and OS X either, // can be changed.
// but it's more difficult to tell. We therefore synchronize this method.
MutexAutoLock mutex(mMutex); MutexAutoLock mutex(mMutex);
nsICollation* coll = getLocaleCollation(); Collator* collator = getCollator();
if (!coll) { if (!collator) {
NS_ERROR("Storage service has no collation"); NS_ERROR("Storage service has no collation");
return 0; return 0;
} }
int32_t res; if (aSensitivity != mLastSensitivity) {
nsresult rv = coll->CompareString(aComparisonStrength, aStr1, aStr2, &res); Collator::Options options{};
if (NS_FAILED(rv)) { options.sensitivity = aSensitivity;
NS_ERROR("Collation compare string failed"); auto result = mCollator->SetOptions(options);
return 0;
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(); mMutex.AssertCurrentThreadOwns();
if (mLocaleCollation) return mLocaleCollation; if (mCollator) {
return mCollator.get();
}
nsCOMPtr<nsICollationFactory> collFact = auto result = mozilla::intl::LocaleService::TryCreateComponent<Collator>();
do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); if (result.isErr()) {
if (!collFact) { NS_WARNING("Could not create mozilla::intl::Collation.");
NS_WARNING("Could not create collation factory");
return nullptr; return nullptr;
} }
nsresult rv = collFact->CreateCollation(getter_AddRefs(mLocaleCollation)); mCollator = result.unwrap();
if (NS_FAILED(rv)) {
NS_WARNING("Could not create collation"); // Sort in a case-insensitive way, where "base" letters are considered
// equal, e.g: a = á, a = A, a ≠ b.
Collator::Options options{};
options.sensitivity = Collator::Sensitivity::Base;
auto optResult = mCollator->SetOptions(options);
if (optResult.isErr()) {
NS_WARNING("Could not configure the mozilla::intl::Collation.");
mCollator = nullptr;
return nullptr; return nullptr;
} }
return mLocaleCollation; return mCollator.get();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -8,17 +8,21 @@
#define MOZSTORAGESERVICE_H #define MOZSTORAGESERVICE_H
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsICollation.h"
#include "nsIFile.h" #include "nsIFile.h"
#include "nsIMemoryReporter.h" #include "nsIMemoryReporter.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/intl/Collator.h"
#include "mozIStorageService.h" #include "mozIStorageService.h"
class nsIMemoryReporter; class nsIMemoryReporter;
struct sqlite3_vfs; struct sqlite3_vfs;
namespace mozilla::intl {
class Collator;
}
namespace mozilla { namespace mozilla {
namespace storage { namespace storage {
@ -40,14 +44,14 @@ class Service : public mozIStorageService,
* The string to be compared against aStr2. * The string to be compared against aStr2.
* @param aStr2 * @param aStr2
* The string to be compared against aStr1. * The string to be compared against aStr1.
* @param aComparisonStrength * @param aSensitivity
* The sorting strength, one of the nsICollation constants. * The sorting sensitivity.
* @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative * @return aStr1 - aStr2. That is, if aStr1 < aStr2, returns a negative
* number. If aStr1 > aStr2, returns a positive number. If * number. If aStr1 > aStr2, returns a positive number. If
* aStr1 == aStr2, returns 0. * aStr1 == aStr2, returns 0.
*/ */
int localeCompareStrings(const nsAString& aStr1, const nsAString& aStr2, int localeCompareStrings(const nsAString& aStr1, const nsAString& aStr2,
int32_t aComparisonStrength); mozilla::intl::Collator::Sensitivity aSensitivity);
static already_AddRefed<Service> getSingleton(); static already_AddRefed<Service> getSingleton();
@ -147,28 +151,30 @@ class Service : public mozIStorageService,
void minimizeMemory(); 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. * 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 * execute outside the lifetime of the Service, this method returns a raw
* pointer. * pointer.
*/ */
nsICollation* getLocaleCollation(); mozilla::intl::Collator* getCollator();
/** /**
* Lazily created collation that all statements of all Connections of this * Lazily created collator that all statements of all Connections of this
* Service may use. The collation is created from the application's locale. * Service may use. The collator is created from the application's locale.
* *
* @note Collation implementations are platform-dependent and in general not * @note The collator is not thread-safe since the options can be changed
* thread-safe. Access to this collation should be synchronized. * between calls. Access should be synchronized.
*/ */
nsCOMPtr<nsICollation> mLocaleCollation; mozilla::UniquePtr<mozilla::intl::Collator> mCollator = nullptr;
nsCOMPtr<nsIFile> mProfileStorageFile; nsCOMPtr<nsIFile> mProfileStorageFile;
nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter; nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter;
static Service* gService; static Service* gService;
mozilla::intl::Collator::Sensitivity mLastSensitivity;
}; };
} // namespace storage } // namespace storage