Skip to content

Commit

Permalink
Bug 1621674 - Unify Localization.jsm, mozILocalization and Localizati…
Browse files Browse the repository at this point in the history
…on WebIDL. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D66531
  • Loading branch information
Zibi Braniecki committed Apr 7, 2020
1 parent dd866e1 commit cbb7b6c
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 151 deletions.
18 changes: 12 additions & 6 deletions dom/base/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3864,7 +3864,7 @@ bool Document::GetAllowPlugins() {
return true;
}

void Document::InitializeLocalization(nsTArray<nsString>& aResourceIds) {
void Document::InitializeLocalization(Sequence<nsString>& aResourceIds) {
MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet");

RefPtr<DocumentL10n> l10n = new DocumentL10n(this);
Expand Down Expand Up @@ -3898,22 +3898,28 @@ void Document::LocalizationLinkAdded(Element* aLinkElement) {
// If the link is added after the DocumentL10n instance
// has been initialized, just pass the resource ID to it.
if (mDocumentL10n) {
AutoTArray<nsString, 1> resourceIds;
resourceIds.AppendElement(href);
Sequence<nsString> resourceIds;
if (NS_WARN_IF(!resourceIds.AppendElement(href, fallible))) {
return;
}
mDocumentL10n->AddResourceIds(resourceIds, false);
} else if (mReadyState >= READYSTATE_INTERACTIVE) {
// Otherwise, if the document has already been parsed
// we need to lazily initialize the localization.
AutoTArray<nsString, 1> resourceIds;
resourceIds.AppendElement(href);
Sequence<nsString> resourceIds;
if (NS_WARN_IF(!resourceIds.AppendElement(href, fallible))) {
return;
}
InitializeLocalization(resourceIds);
mDocumentL10n->TriggerInitialDocumentTranslation();
} else {
// Otherwise, we're still parsing the document.
// In that case, add it to the pending list. This list
// will be resolved once the end of l10n resource
// container is reached.
mL10nResources.AppendElement(href);
if (NS_WARN_IF(!mL10nResources.AppendElement(href, fallible))) {
return;
}

if (!mPendingInitialTranslation) {
// Our initial translation is going to block layout start. Make sure we
Expand Down
4 changes: 2 additions & 2 deletions dom/base/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -3734,7 +3734,7 @@ class Document : public nsINode,
private:
bool IsErrorPage() const;

void InitializeLocalization(nsTArray<nsString>& aResourceIds);
void InitializeLocalization(Sequence<nsString>& aResourceIds);

// Takes the bits from mStyleUseCounters if appropriate, and sets them in
// mUseCounters.
Expand All @@ -3760,7 +3760,7 @@ class Document : public nsINode,

FlashClassification DocumentFlashClassificationInternal();

nsTArray<nsString> mL10nResources;
Sequence<nsString> mL10nResources;

// The application cache that this document is associated with, if
// any. This can change during the lifetime of the document.
Expand Down
2 changes: 1 addition & 1 deletion dom/bindings/Bindings.conf
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ DOMInterfaces = {
},

'Localization': {
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages' ],
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages', 'formatValueSync', 'formatValuesSync', 'formatMessagesSync' ],
'nativeType': 'mozilla::intl::Localization',
},

Expand Down
16 changes: 11 additions & 5 deletions dom/chrome-webidl/DOMLocalization.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ interface DOMLocalization : Localization {
* - aResourceids - a list of localization resource URIs
* which will provide messages for this
* Localization instance.
* - aGenerateMessages - a callback function which will be
* used to generate an iterator
* over FluentBundle instances.
* - aSync - Specifies if the initial state of the DOMLocalization
* and the underlying Localization API is synchronous.
* This enables a number of synchronous methods on the
* Localization API and uses it for `TranslateElements`
* making the method return a synchronusly resolved promise.
* - aBundleGenerator - an object with two methods - `generateBundles` and
* `generateBundlesSync` allowing consumers to overload the
* default generators provided by Gecko.
*/
[Throws]
constructor(optional sequence<DOMString> aResourceIds,
optional GenerateMessages aGenerateMessages);
constructor(sequence<DOMString> aResourceIds,
optional boolean aSync = false,
optional BundleGenerator aBundleGenerator = {});

/**
* Adds a node to nodes observed for localization
Expand Down
42 changes: 33 additions & 9 deletions dom/chrome-webidl/Localization.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,18 @@ dictionary L10nMessage {
* and produces an iterator over FluentBundle objects used for
* localization with fallbacks.
*/
callback GenerateMessages = Promise<any> (sequence<DOMString> aResourceIds);
callback GenerateBundles = Promise<any> (sequence<DOMString> aResourceIds);
callback GenerateBundlesSync = any (sequence<DOMString> aResourceIds);

/**
* The structure provides custom methods for the Localization API that
* will be used to generate the `FluentBundle` iterator.
* This allows the consumer to overload the default Gecko generator.
*/
dictionary BundleGenerator {
GenerateBundles generateBundles;
GenerateBundlesSync generateBundlesSync;
};

/**
* Localization is an implementation of the Fluent Localization API.
Expand All @@ -64,16 +75,20 @@ callback GenerateMessages = Promise<any> (sequence<DOMString> aResourceIds);
interface Localization {
/**
* Constructor arguments:
* - aResourceids - a list of localization resource URIs
* which will provide messages for this
* Localization instance.
* - aGenerateMessages - a callback function which will be
* used to generate an iterator
* over FluentBundle instances.
* - aResourceids - a list of localization resource URIs
* which will provide messages for this
* Localization instance.
* - aSync - Specifies if the initial state of the Localization API is synchronous.
* This enables a number of synchronous methods on the
* Localization API.
* - aBundleGenerator - an object with two methods - `generateBundles` and
* `generateBundlesSync` allowing consumers to overload the
* default generators provided by Gecko.
*/
[Throws]
constructor(optional sequence<DOMString> aResourceIds,
optional GenerateMessages aGenerateMessages);
constructor(sequence<DOMString> aResourceIds,
optional boolean aSync = false,
optional BundleGenerator aBundleGenerator = {});

/**
* A method for adding resources to the localization context.
Expand Down Expand Up @@ -137,6 +152,15 @@ interface Localization {
* ]);
*/
[NewObject] Promise<sequence<L10nMessage>> formatMessages(sequence<L10nKey> aKeys);

void setIsSync(boolean aIsSync);

[NewObject, Throws]
UTF8String? formatValueSync(UTF8String aId, optional L10nArgs aArgs);
[NewObject, Throws]
sequence<UTF8String?> formatValuesSync(sequence<L10nKey> aKeys);
[NewObject, Throws]
sequence<L10nMessage?> formatMessagesSync(sequence<L10nKey> aKeys);
};

/**
Expand Down
84 changes: 25 additions & 59 deletions dom/l10n/DOMLocalization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ DOMLocalization::DOMLocalization(nsIGlobalObject* aGlobal)
}

already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
Expand All @@ -49,20 +48,7 @@ already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
}

RefPtr<DOMLocalization> domLoc = new DOMLocalization(global);
nsTArray<nsString> resourceIds;

if (aResourceIds.WasPassed()) {
resourceIds = aResourceIds.Value();
}

if (aGenerateMessages.WasPassed()) {
GenerateMessages& generateMessages = aGenerateMessages.Value();
JS::Rooted<JS::Value> generateMessagesJS(
aGlobal.Context(), JS::ObjectValue(*generateMessages.CallbackOrNull()));
domLoc->Init(resourceIds, generateMessagesJS, aRv);
} else {
domLoc->Init(resourceIds, aRv);
}
domLoc->Init(aResourceIds, aSync, aBundleGenerator, aRv);

if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
Expand Down Expand Up @@ -191,7 +177,7 @@ class ElementTranslationHandler : public PromiseNativeHandler {
JS::Handle<JS::Value> aValue) override {
ErrorResult rv;

nsTArray<L10nMessage> l10nData;
nsTArray<Nullable<L10nMessage>> l10nData;
if (aValue.isObject()) {
JS::ForOfIterator iter(aCx);
if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) {
Expand All @@ -215,15 +201,18 @@ class ElementTranslationHandler : public PromiseNativeHandler {
break;
}

L10nMessage* slotPtr = l10nData.AppendElement(mozilla::fallible);
Nullable<L10nMessage>* slotPtr =
l10nData.AppendElement(mozilla::fallible);
if (!slotPtr) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
}

if (!slotPtr->Init(aCx, temp)) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
if (!temp.isNull()) {
if (!slotPtr->SetValue().Init(aCx, temp)) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
}
}
}
}
Expand Down Expand Up @@ -325,39 +314,11 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
}

if (mIsSync) {
nsTArray<JS::Value> jsKeys;
SequenceRooter<JS::Value> keysRooter(cx, &jsKeys);
for (auto& key : l10nKeys) {
JS::RootedValue jsKey(cx);
if (!ToJSValue(cx, key, &jsKey)) {
aRv.NoteJSContextException(cx);
return nullptr;
}
jsKeys.AppendElement(jsKey);
}

nsTArray<JS::Value> messages;
SequenceRooter<JS::Value> messagesRooter(cx, &messages);
mLocalization->FormatMessagesSync(jsKeys, messages);
nsTArray<L10nMessage> l10nData;
SequenceRooter<L10nMessage> l10nDataRooter(cx, &l10nData);

for (auto& msg : messages) {
JS::Rooted<JS::Value> rootedMsg(cx);
rootedMsg.set(msg);
L10nMessage* slotPtr = l10nData.AppendElement(mozilla::fallible);
if (!slotPtr) {
promise->MaybeRejectWithUndefined();
return MaybeWrapPromise(promise);
}
nsTArray<Nullable<L10nMessage>> l10nMessages;

if (!slotPtr->Init(cx, rootedMsg)) {
promise->MaybeRejectWithUndefined();
return MaybeWrapPromise(promise);
}
}
FormatMessagesSync(cx, l10nKeys, l10nMessages, aRv);

ApplyTranslations(domElements, l10nData, aProto, aRv);
ApplyTranslations(domElements, l10nMessages, aProto, aRv);
if (NS_WARN_IF(aRv.Failed())) {
promise->MaybeRejectWithUndefined();
return MaybeWrapPromise(promise);
Expand Down Expand Up @@ -481,10 +442,10 @@ void DOMLocalization::SetRootInfo(Element* aElement) {
aElement->SetAttr(kNameSpaceID_None, dirAtom, dir, true);
}

void DOMLocalization::ApplyTranslations(nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<L10nMessage>& aTranslations,
nsXULPrototypeDocument* aProto,
ErrorResult& aRv) {
void DOMLocalization::ApplyTranslations(
nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<Nullable<L10nMessage>>& aTranslations,
nsXULPrototypeDocument* aProto, ErrorResult& aRv) {
if (aElements.Length() != aTranslations.Length()) {
aRv.Throw(NS_ERROR_FAILURE);
return;
Expand All @@ -499,15 +460,20 @@ void DOMLocalization::ApplyTranslations(nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<L10nOverlaysError> errors;
for (size_t i = 0; i < aTranslations.Length(); ++i) {
Element* elem = aElements[i];
L10nOverlays::TranslateElement(*elem, aTranslations[i], errors, aRv);
if (aTranslations[i].IsNull()) {
continue;
}
L10nOverlays::TranslateElement(*elem, aTranslations[i].Value(), errors,
aRv);
if (NS_WARN_IF(aRv.Failed())) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (aProto) {
// We only need to rebuild deep if the translation has a value.
// Otherwise we'll only rebuild the attributes.
aProto->RebuildL10nPrototype(elem, !aTranslations[i].mValue.IsVoid());
aProto->RebuildL10nPrototype(elem,
!aTranslations[i].Value().mValue.IsVoid());
}
}

Expand Down
7 changes: 3 additions & 4 deletions dom/l10n/DOMLocalization.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ class DOMLocalization : public intl::Localization {
explicit DOMLocalization(nsIGlobalObject* aGlobal);

static already_AddRefed<DOMLocalization> Constructor(
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
ErrorResult& aRv);

virtual JSObject* WrapObject(JSContext* aCx,
Expand Down Expand Up @@ -87,7 +86,7 @@ class DOMLocalization : public intl::Localization {
* the localized elements.
*/
void ApplyTranslations(nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<L10nMessage>& aTranslations,
nsTArray<Nullable<L10nMessage>>& aTranslations,
nsXULPrototypeDocument* aProto, ErrorResult& aRv);

bool SubtreeRootInRoots(nsINode* aSubtreeRoot) {
Expand Down
5 changes: 3 additions & 2 deletions dom/l10n/DocumentL10n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ DocumentL10n::DocumentL10n(Document* aDocument)
}
}

void DocumentL10n::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
DOMLocalization::Init(aResourceIds, aRv);
void DocumentL10n::Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv) {
DOMLocalization::Init(aResourceIds, mIsSync, {}, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}

mReady = Promise::Create(mGlobal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
Expand Down
2 changes: 1 addition & 1 deletion dom/l10n/DocumentL10n.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DocumentL10n final : public DOMLocalization {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentL10n, DOMLocalization)

explicit DocumentL10n(Document* aDocument);
void Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv);
void Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv);

protected:
virtual ~DocumentL10n() = default;
Expand Down
Loading

0 comments on commit cbb7b6c

Please sign in to comment.