mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1560342 - Replace generic object
argument with a specific L10nArgs object for passing l10n arguments. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D35586 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
ae32fa44eb
commit
224a1bb5c4
@ -232,6 +232,10 @@ DOMInterfaces = {
|
||||
'implicitJSContext': [ 'filename', 'lineNumber', 'stack' ],
|
||||
},
|
||||
|
||||
'DOMLocalization': {
|
||||
'implicitJSContext': [ 'getAttributes' ],
|
||||
},
|
||||
|
||||
'DOMMatrixReadOnly': {
|
||||
'headerFile': 'mozilla/dom/DOMMatrix.h',
|
||||
},
|
||||
@ -500,10 +504,10 @@ DOMInterfaces = {
|
||||
},
|
||||
|
||||
'Localization': {
|
||||
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages' ],
|
||||
'nativeType': 'mozilla::intl::Localization',
|
||||
},
|
||||
|
||||
|
||||
'MatchGlob': {
|
||||
'nativeType': 'mozilla::extensions::MatchGlob',
|
||||
},
|
||||
|
@ -4,21 +4,31 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* An object used to carry localization information from and to an
|
||||
* Element.
|
||||
* L10nKey is an object used to carry localization tuple for message
|
||||
* translation.
|
||||
*
|
||||
* Fields:
|
||||
* id - identifier of a message used to localize the element.
|
||||
* args - an optional map of arguments used to format the message.
|
||||
* The argument will be converted to/from JSON, so can
|
||||
* handle any value that JSON can, but in practice, the API
|
||||
* will only use a string or a number for now.
|
||||
* id - identifier of a message.
|
||||
* args - an optional record of arguments used to format the message.
|
||||
* The argument will be converted to/from JSON, and the API
|
||||
* will only handle strings and numbers.
|
||||
*/
|
||||
typedef record<DOMString, (DOMString or double)?> L10nArgs;
|
||||
|
||||
dictionary L10nKey {
|
||||
DOMString? id = null;
|
||||
object? args = null;
|
||||
L10nArgs? args = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* L10nMessage is a compound translation unit from Fluent which
|
||||
* encodes the value and (optionall) a list of attributes used
|
||||
* to translate a given widget.
|
||||
*
|
||||
* Most simple imperative translations will only use the `value`,
|
||||
* but when building a Message for a UI widget, a combination
|
||||
* of a value and attributes will be used.
|
||||
*/
|
||||
dictionary AttributeNameValue {
|
||||
required DOMString name;
|
||||
required DOMString value;
|
||||
@ -29,6 +39,11 @@ dictionary L10nMessage {
|
||||
sequence<AttributeNameValue>? attributes = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* A callback function which takes a list of locales and a list
|
||||
* of localization resources and produces an iterator over
|
||||
* FluentBundle objects used for localization with fallbacks.
|
||||
*/
|
||||
callback GenerateMessages = Promise<any> (sequence<DOMString> aAppLocales, sequence<DOMString> aResourceIds);
|
||||
|
||||
/**
|
||||
@ -82,7 +97,7 @@ interface Localization {
|
||||
* let value = await document.l10n.formatValue("unread-emails", {count: 5});
|
||||
* assert.equal(value, "You have 5 unread emails");
|
||||
*/
|
||||
[NewObject] Promise<DOMString> formatValue(DOMString aId, optional object aArgs);
|
||||
[NewObject] Promise<DOMString> formatValue(DOMString aId, optional L10nArgs aArgs);
|
||||
|
||||
/**
|
||||
* Formats values of a list of messages with given ids.
|
||||
@ -121,4 +136,11 @@ interface Localization {
|
||||
* ]);
|
||||
*/
|
||||
[NewObject] Promise<sequence<L10nMessage>> formatMessages(sequence<L10nKey> aKeys);
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper dict for converting between JS Value and L10nArgs.
|
||||
*/
|
||||
dictionary L10nArgsHelperDict {
|
||||
required L10nArgs args;
|
||||
};
|
@ -148,16 +148,7 @@ void DOMLocalization::GetAttributes(JSContext* aCx, Element& aElement,
|
||||
}
|
||||
|
||||
if (aElement.GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, l10nArgs)) {
|
||||
JS::Rooted<JS::Value> json(aCx);
|
||||
if (!JS_ParseJSON(aCx, l10nArgs.get(), l10nArgs.Length(), &json)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
if (!json.isObject()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
aResult.mArgs = &json.toObject();
|
||||
ConvertStringToL10nArgs(aCx, l10nArgs, aResult.mArgs.SetValue(), aRv);
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +279,7 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoEntryScript aes(mGlobal, "DOMLocalization GetAttributes");
|
||||
AutoEntryScript aes(mGlobal, "DOMLocalization TranslateElements");
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
for (auto& domElement : aElements) {
|
||||
@ -303,8 +294,7 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||
}
|
||||
|
||||
GetAttributes(cx, *domElement, *key, aRv);
|
||||
if (aRv.Failed()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -534,3 +524,29 @@ void DOMLocalization::ReportL10nOverlaysErrors(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DOMLocalization::ConvertStringToL10nArgs(JSContext* aCx,
|
||||
const nsString& aInput,
|
||||
intl::L10nArgs& aRetVal,
|
||||
ErrorResult& aRv) {
|
||||
// This method uses a temporary dictionary to automate
|
||||
// converting a JSON string into an IDL Record via a dictionary.
|
||||
//
|
||||
// Once we get Record::Init(const nsAString& aJSON), we'll switch to
|
||||
// that.
|
||||
L10nArgsHelperDict helperDict;
|
||||
if (!helperDict.Init(NS_LITERAL_STRING("{\"args\": ") + aInput +
|
||||
NS_LITERAL_STRING("}"))) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
for (auto& entry : helperDict.mArgs.Entries()) {
|
||||
L10nArgs::EntryType* newEntry = aRetVal.Entries().AppendElement(fallible);
|
||||
if (!newEntry) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
newEntry->mKey = entry.mKey;
|
||||
newEntry->mValue = entry.mValue;
|
||||
}
|
||||
}
|
@ -83,6 +83,8 @@ class DOMLocalization : public intl::Localization {
|
||||
void DisconnectMutations();
|
||||
void DisconnectRoots();
|
||||
void ReportL10nOverlaysErrors(nsTArray<L10nOverlaysError>& aErrors);
|
||||
void ConvertStringToL10nArgs(JSContext* aCx, const nsString& aInput,
|
||||
intl::L10nArgs& aRetVal, ErrorResult& aRv);
|
||||
|
||||
RefPtr<L10nMutations> mMutations;
|
||||
nsTHashtable<nsRefPtrHashKey<Element>> mRoots;
|
||||
@ -91,4 +93,4 @@ class DOMLocalization : public intl::Localization {
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
#endif
|
@ -51,4 +51,4 @@
|
||||
|
||||
<p id="label1"></p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
@ -171,19 +171,22 @@ uint32_t Localization::RemoveResourceIds(
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Localization::FormatValue(
|
||||
JSContext* aCx, const nsAString& aId,
|
||||
const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv) {
|
||||
JSContext* aCx, const nsAString& aId, const Optional<L10nArgs>& aArgs,
|
||||
ErrorResult& aRv) {
|
||||
JS::Rooted<JS::Value> args(aCx);
|
||||
|
||||
if (aArgs.WasPassed()) {
|
||||
args = JS::ObjectValue(*aArgs.Value());
|
||||
ConvertL10nArgsToJSValue(aCx, aArgs.Value(), &args, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
args = JS::UndefinedValue();
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
nsresult rv = mLocalization->FormatValue(aId, args, getter_AddRefs(promise));
|
||||
if (NS_FAILED(rv)) {
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
@ -205,7 +208,7 @@ already_AddRefed<Promise> Localization::FormatValues(
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
aRv = mLocalization->FormatValues(jsKeys, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -227,7 +230,7 @@ already_AddRefed<Promise> Localization::FormatMessages(
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
aRv = mLocalization->FormatMessages(jsKeys, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -301,4 +304,34 @@ already_AddRefed<Promise> Localization::MaybeWrapPromise(
|
||||
RefPtr<PromiseResolver> resolver = new PromiseResolver(docPromise);
|
||||
aInnerPromise->AppendNativeHandler(resolver);
|
||||
return docPromise.forget();
|
||||
}
|
||||
|
||||
void Localization::ConvertL10nArgsToJSValue(
|
||||
JSContext* aCx, const L10nArgs& aArgs, JS::MutableHandle<JS::Value> aRetVal,
|
||||
ErrorResult& aRv) {
|
||||
// This method uses a temporary dictionary to automate
|
||||
// converting an IDL Record to a JS Value via a dictionary.
|
||||
//
|
||||
// Once we get ToJSValue for Record, we'll switch to that.
|
||||
L10nArgsHelperDict helperDict;
|
||||
for (auto& entry : aArgs.Entries()) {
|
||||
L10nArgs::EntryType* newEntry =
|
||||
helperDict.mArgs.Entries().AppendElement(fallible);
|
||||
if (!newEntry) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
newEntry->mKey = entry.mKey;
|
||||
newEntry->mValue = entry.mValue;
|
||||
}
|
||||
JS::Rooted<JS::Value> jsVal(aCx);
|
||||
if (!ToJSValue(aCx, helperDict, &jsVal)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSObject*> jsObj(aCx, &jsVal.toObject());
|
||||
if (!JS_GetProperty(aCx, jsObj, "args", aRetVal)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ using namespace mozilla::dom;
|
||||
namespace mozilla {
|
||||
namespace intl {
|
||||
|
||||
typedef Record<nsString, Nullable<OwningStringOrDouble>> L10nArgs;
|
||||
|
||||
class Localization : public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
public nsWrapperCache {
|
||||
@ -49,9 +51,9 @@ class Localization : public nsIObserver,
|
||||
|
||||
uint32_t RemoveResourceIds(const nsTArray<nsString>& aResourceIds);
|
||||
|
||||
already_AddRefed<Promise> FormatValue(
|
||||
JSContext* aCx, const nsAString& aId,
|
||||
const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> FormatValue(JSContext* aCx, const nsAString& aId,
|
||||
const Optional<L10nArgs>& aArgs,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> FormatValues(JSContext* aCx,
|
||||
const Sequence<L10nKey>& aKeys,
|
||||
@ -66,6 +68,9 @@ class Localization : public nsIObserver,
|
||||
void RegisterObservers();
|
||||
void OnChange();
|
||||
already_AddRefed<Promise> MaybeWrapPromise(Promise* aInnerPromise);
|
||||
void ConvertL10nArgsToJSValue(JSContext* aCx, const L10nArgs& aArgs,
|
||||
JS::MutableHandle<JS::Value> aRetVal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsCOMPtr<mozILocalization> mLocalization;
|
||||
|
Loading…
Reference in New Issue
Block a user