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 11:46:37 +00:00
Родитель a7f0aa2461
Коммит ee5176c26e
4 изменённых файлов: 85 добавлений и 61 удалений

Просмотреть файл

@ -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<const char*>(aStr1), aLen1);
NS_ConvertUTF8toUTF16 str2(static_cast<const char*>(aStr2), aLen2);
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.
*
* @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<const char16_t*>(aStr1);
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 str2(buf2, buf2 + (aLen2 / sizeof(char16_t)));
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
@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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<nsICollationFactory> collFact =
do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
if (!collFact) {
NS_WARNING("Could not create collation factory");
auto result = mozilla::intl::LocaleService::TryCreateComponent<Collator>();
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();
}
////////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -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<Service> 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<nsICollation> mLocaleCollation;
mozilla::UniquePtr<mozilla::intl::Collator> mCollator = nullptr;
nsCOMPtr<nsIFile> mProfileStorageFile;
nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter;
static Service* gService;
mozilla::intl::Collator::Sensitivity mLastSensitivity;
};
} // namespace storage