From 8066c9d047cb5047009f90a8ce26e3cdc07de32b Mon Sep 17 00:00:00 2001 From: Greg Tatum Date: Tue, 10 Aug 2021 11:46:36 +0000 Subject: [PATCH] Bug 1719550 - Unify collator in txXPathResultComparator; r=platform-i18n-reviewers,nordzilla Differential Revision: https://phabricator.services.mozilla.com/D121430 --- dom/xslt/xslt/txXPathResultComparator.cpp | 150 +++++++++++++++------- dom/xslt/xslt/txXPathResultComparator.h | 9 +- 2 files changed, 109 insertions(+), 50 deletions(-) diff --git a/dom/xslt/xslt/txXPathResultComparator.cpp b/dom/xslt/xslt/txXPathResultComparator.cpp index e74b7f7ffe26..cab67c599ae2 100644 --- a/dom/xslt/xslt/txXPathResultComparator.cpp +++ b/dom/xslt/xslt/txXPathResultComparator.cpp @@ -4,14 +4,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/FloatingPoint.h" +#include "mozilla/intl/Collator.h" +#include "mozilla/intl/LocaleService.h" #include "txXPathResultComparator.h" #include "txExpr.h" #include "nsComponentManagerUtils.h" #include "txCore.h" -#include "nsCollationCID.h" using namespace mozilla; +using Collator = mozilla::intl::Collator; #define kAscending (1 << 0) #define kUpperFirst (1 << 1) @@ -27,30 +29,89 @@ txResultStringComparator::txResultStringComparator(bool aAscending, } nsresult txResultStringComparator::init(const nsString& aLanguage) { - nsresult rv; + auto result = + aLanguage.IsEmpty() + ? mozilla::intl::LocaleService::TryCreateComponent() + : Collator::TryCreate(NS_ConvertUTF16toUTF8(aLanguage).get()); - nsCOMPtr colFactory = - do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE); + auto collator = result.unwrap(); - if (aLanguage.IsEmpty()) { - rv = colFactory->CreateCollation(getter_AddRefs(mCollation)); - } else { - rv = colFactory->CreateCollationForLocale(NS_ConvertUTF16toUTF8(aLanguage), - getter_AddRefs(mCollation)); - } - - NS_ENSURE_SUCCESS(rv, rv); + // Sort in a case-insensitive way, where "base" letters are considered + // equal, e.g: a = á, a = A, a ≠ b. + auto optResult = collator->SetOptions( + Collator::Options{.sensitivity = Collator::Sensitivity::Base}); + NS_ENSURE_TRUE(optResult.isOk(), NS_ERROR_FAILURE); + mCollator = UniquePtr(collator.release()); return NS_OK; } +namespace mozilla::intl { + +/** + * mozilla::intl APIs require sizeable buffers. This class abstracts over + * the nsTArray. + */ +class nsTArrayU8Buffer { + public: + using CharType = uint8_t; + + // Do not allow copy or move. Move could be added in the future if needed. + nsTArrayU8Buffer(const nsTArrayU8Buffer&) = delete; + nsTArrayU8Buffer& operator=(const nsTArrayU8Buffer&) = delete; + + explicit nsTArrayU8Buffer(nsTArray& aArray) : mArray(aArray) {} + + /** + * Ensures the buffer has enough space to accommodate |size| elements. + */ + [[nodiscard]] bool reserve(size_t size) { + mArray.SetCapacity(size); + // nsTArray::SetCapacity returns void, return true to keep the API the same + // as the other Buffer implementations. + return true; + } + + /** + * Returns the raw data inside the buffer. + */ + CharType* data() { return mArray.Elements(); } + + /** + * Returns the count of elements written into the buffer. + */ + size_t length() const { return mArray.Length(); } + + /** + * Returns the buffer's overall capacity. + */ + size_t capacity() const { return mArray.Capacity(); } + + /** + * Resizes the buffer to the given amount of written elements. + */ + void written(size_t amount) { + MOZ_ASSERT(amount <= mArray.Capacity()); + // This sets |mArray|'s internal size so that it matches how much was + // written. This is necessary because the write happens across FFI + // boundaries. + mArray.SetLengthAndRetainStorage(amount); + } + + private: + nsTArray& mArray; +}; +} // namespace mozilla::intl + nsresult txResultStringComparator::createSortableValue(Expr* aExpr, txIEvalContext* aContext, txObject*& aResult) { UniquePtr val(new StringValue); - if (!mCollation) return NS_ERROR_FAILURE; + if (!mCollator) { + return NS_ERROR_FAILURE; + } val->mCaseKeyString = MakeUnique(); nsString& nsCaseKey = *val->mCaseKeyString; @@ -63,9 +124,9 @@ nsresult txResultStringComparator::createSortableValue(Expr* aExpr, return NS_OK; } - rv = mCollation->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive, - nsCaseKey, val->mKey); - NS_ENSURE_SUCCESS(rv, rv); + mozilla::intl::nsTArrayU8Buffer buffer(val->mKey); + auto result = mCollator->GetSortKey(nsCaseKey, buffer); + NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE); aResult = val.release(); @@ -76,45 +137,43 @@ int txResultStringComparator::compareValues(txObject* aVal1, txObject* aVal2) { StringValue* strval1 = (StringValue*)aVal1; StringValue* strval2 = (StringValue*)aVal2; - if (!mCollation) return -1; - - if (strval1->mKey.Length() == 0) { - if (strval2->mKey.Length() == 0) return 0; - return ((mSorting & kAscending) ? -1 : 1); - } - - if (strval2->mKey.Length() == 0) return ((mSorting & kAscending) ? 1 : -1); - - nsresult rv; - int32_t result = -1; - rv = mCollation->CompareRawSortKey(strval1->mKey, strval2->mKey, &result); - if (NS_FAILED(rv)) { - // XXX ErrorReport + if (!mCollator) { return -1; } - if (result != 0) return ((mSorting & kAscending) ? 1 : -1) * result; + if (strval1->mKey.Length() == 0) { + if (strval2->mKey.Length() == 0) { + return 0; + } + return ((mSorting & kAscending) ? -1 : 1); + } + + if (strval2->mKey.Length() == 0) { + return ((mSorting & kAscending) ? 1 : -1); + } + + nsresult rv; + int32_t result = mCollator->CompareSortKeys(strval1->mKey, strval2->mKey); + + if (result != 0) { + return ((mSorting & kAscending) ? 1 : -1) * result; + } if (strval1->mCaseKeyString && strval1->mKey.Length() != 0) { - rv = strval1->initCaseKey(mCollation); + rv = strval1->initCaseKey(*mCollator); if (NS_FAILED(rv)) { // XXX ErrorReport return -1; } } if (strval2->mCaseKeyString && strval2->mKey.Length() != 0) { - rv = strval2->initCaseKey(mCollation); + rv = strval2->initCaseKey(*mCollator); if (NS_FAILED(rv)) { // XXX ErrorReport return -1; } } - rv = mCollation->CompareRawSortKey(strval1->mCaseKey, strval2->mCaseKey, - &result); - if (NS_FAILED(rv)) { - // XXX ErrorReport - return -1; - } + result = mCollator->CompareSortKeys(strval1->mCaseKey, strval2->mCaseKey); return ((mSorting & kAscending) ? 1 : -1) * ((mSorting & kUpperFirst) ? -1 : 1) * result; @@ -125,12 +184,13 @@ txResultStringComparator::StringValue::StringValue() = default; txResultStringComparator::StringValue::~StringValue() = default; nsresult txResultStringComparator::StringValue::initCaseKey( - nsICollation* aCollation) { - nsresult rv = aCollation->AllocateRawSortKey( - nsICollation::kCollationCaseSensitive, *mCaseKeyString, mCaseKey); - if (NS_FAILED(rv)) { + const mozilla::intl::Collator& aCollator) { + mozilla::intl::nsTArrayU8Buffer buffer(mCaseKey); + + auto result = aCollator.GetSortKey(*mCaseKeyString, buffer); + if (result.isErr()) { mCaseKey.SetLength(0); - return rv; + return NS_ERROR_FAILURE; } mCaseKeyString = nullptr; diff --git a/dom/xslt/xslt/txXPathResultComparator.h b/dom/xslt/xslt/txXPathResultComparator.h index 3877ad7d1ce2..273bbf4f1abb 100644 --- a/dom/xslt/xslt/txXPathResultComparator.h +++ b/dom/xslt/xslt/txXPathResultComparator.h @@ -7,10 +7,11 @@ #define TRANSFRMX_XPATHRESULTCOMPARATOR_H #include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "mozilla/intl/Collator.h" #include "mozilla/UniquePtr.h" #include "txCore.h" #include "nsCOMPtr.h" -#include "nsICollation.h" #include "nsString.h" class Expr; @@ -49,10 +50,8 @@ class txResultStringComparator : public txXPathResultComparator { txObject*& aResult) override; private: - nsCOMPtr mCollation; + mozilla::UniquePtr mCollator; nsresult init(const nsString& aLanguage); - nsresult createRawSortKey(const int32_t aStrength, const nsString& aString, - uint8_t** aKey, uint32_t* aLength); int mSorting; class StringValue : public txObject { @@ -60,7 +59,7 @@ class txResultStringComparator : public txXPathResultComparator { StringValue(); ~StringValue(); - nsresult initCaseKey(nsICollation* aCollation); + nsresult initCaseKey(const mozilla::intl::Collator& aCollator); nsTArray mKey; // Either mCaseKeyString is non-null, or we have a usable key in mCaseKey