Backed out 3 changesets (bug 1621674) for causing xpcshell permafailures in browser/extensions/formautofill/test/unit/test_createRecords.js CLOSED TREE

Backed out changeset f45de944f20a (bug 1621674)
Backed out changeset 0b2fdf331bce (bug 1621674)
Backed out changeset 2f7e9c807020 (bug 1621674)
This commit is contained in:
Stefan Hindli 2020-04-08 00:49:16 +03:00
parent 73df276d3c
commit 37b364e264
43 changed files with 233 additions and 344 deletions

View File

@ -45,7 +45,7 @@ class _BookmarkPanelHub {
this._handleMessageRequest = handleMessageRequest;
this._addImpression = addImpression;
this._dispatch = dispatch;
this._l10n = new DOMLocalization([]);
this._l10n = new DOMLocalization();
this._initialized = true;
}

View File

@ -69,10 +69,9 @@ class _RemoteL10n {
"browser/branding/sync-brand.ftl",
"branding/brand.ftl",
],
false,
Services.prefs.getBoolPref(USE_REMOTE_L10N_PREF, true)
? { generateBundles }
: {}
? generateBundles
: undefined
);
}

View File

@ -55,36 +55,30 @@ describe("RemoteL10n", () => {
assert.calledOnce(domL10nStub);
const { args } = domL10nStub.firstCall;
// The first arg is the resource array,
// the second one is false (use async),
// and the third one is the bundle generator.
assert.equal(args.length, 3);
// The first arg is the resource array, and the second one is the bundle generator.
assert.equal(args.length, 2);
assert.deepEqual(args[0], [
"browser/newtab/asrouter.ftl",
"browser/branding/brandings.ftl",
"browser/branding/sync-brand.ftl",
"branding/brand.ftl",
]);
assert.isFalse(args[1]);
assert.isFunction(args[2].generateBundles);
assert.isFunction(args[1]);
});
it("should load the local Fluent file if USE_REMOTE_L10N_PREF is false", () => {
sandbox.stub(global.Services.prefs, "getBoolPref").returns(false);
RemoteL10n._createDOML10n();
const { args } = domL10nStub.firstCall;
// The first arg is the resource array,
// the second one is false (use async),
// and the third one is null.
assert.equal(args.length, 3);
// The first arg is the resource array, and the second one should be null.
assert.equal(args.length, 2);
assert.deepEqual(args[0], [
"browser/newtab/asrouter.ftl",
"browser/branding/brandings.ftl",
"browser/branding/sync-brand.ftl",
"branding/brand.ftl",
]);
assert.isFalse(args[1]);
assert.isEmpty(args[2]);
assert.isUndefined(args[1]);
});
});
});

View File

@ -249,8 +249,7 @@ function getBundleForLocales(newLocales) {
}
return new Localization(
["browser/preferences/preferences.ftl", "branding/brand.ftl"],
false,
{ generateBundles }
generateBundles
);
}

View File

@ -3864,7 +3864,7 @@ bool Document::GetAllowPlugins() {
return true;
}
void Document::InitializeLocalization(Sequence<nsString>& aResourceIds) {
void Document::InitializeLocalization(nsTArray<nsString>& aResourceIds) {
MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet");
RefPtr<DocumentL10n> l10n = new DocumentL10n(this);
@ -3898,18 +3898,14 @@ 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) {
Sequence<nsString> resourceIds;
if (NS_WARN_IF(!resourceIds.AppendElement(href, fallible))) {
return;
}
AutoTArray<nsString, 1> resourceIds;
resourceIds.AppendElement(href);
mDocumentL10n->AddResourceIds(resourceIds, false);
} else if (mReadyState >= READYSTATE_INTERACTIVE) {
// Otherwise, if the document has already been parsed
// we need to lazily initialize the localization.
Sequence<nsString> resourceIds;
if (NS_WARN_IF(!resourceIds.AppendElement(href, fallible))) {
return;
}
AutoTArray<nsString, 1> resourceIds;
resourceIds.AppendElement(href);
InitializeLocalization(resourceIds);
mDocumentL10n->TriggerInitialDocumentTranslation();
} else {
@ -3917,9 +3913,7 @@ void Document::LocalizationLinkAdded(Element* aLinkElement) {
// In that case, add it to the pending list. This list
// will be resolved once the end of l10n resource
// container is reached.
if (NS_WARN_IF(!mL10nResources.AppendElement(href, fallible))) {
return;
}
mL10nResources.AppendElement(href);
if (!mPendingInitialTranslation) {
// Our initial translation is going to block layout start. Make sure we

View File

@ -3734,7 +3734,7 @@ class Document : public nsINode,
private:
bool IsErrorPage() const;
void InitializeLocalization(Sequence<nsString>& aResourceIds);
void InitializeLocalization(nsTArray<nsString>& aResourceIds);
// Takes the bits from mStyleUseCounters if appropriate, and sets them in
// mUseCounters.
@ -3760,7 +3760,7 @@ class Document : public nsINode,
FlashClassification DocumentFlashClassificationInternal();
Sequence<nsString> mL10nResources;
nsTArray<nsString> mL10nResources;
// The application cache that this document is associated with, if
// any. This can change during the lifetime of the document.

View File

@ -459,7 +459,7 @@ DOMInterfaces = {
},
'Localization': {
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages', 'formatValueSync', 'formatValuesSync', 'formatMessagesSync' ],
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages' ],
'nativeType': 'mozilla::intl::Localization',
},

View File

@ -31,19 +31,13 @@ interface DOMLocalization : Localization {
* - aResourceids - a list of localization resource URIs
* which will provide messages for this
* Localization instance.
* - 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.
* - aGenerateMessages - a callback function which will be
* used to generate an iterator
* over FluentBundle instances.
*/
[Throws]
constructor(sequence<DOMString> aResourceIds,
optional boolean aSync = false,
optional BundleGenerator aBundleGenerator = {});
constructor(optional sequence<DOMString> aResourceIds,
optional GenerateMessages aGenerateMessages);
/**
* Adds a node to nodes observed for localization

View File

@ -42,18 +42,7 @@ dictionary L10nMessage {
* and produces an iterator over FluentBundle objects used for
* localization with fallbacks.
*/
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;
};
callback GenerateMessages = Promise<any> (sequence<DOMString> aResourceIds);
/**
* Localization is an implementation of the Fluent Localization API.
@ -75,20 +64,16 @@ dictionary BundleGenerator {
interface Localization {
/**
* Constructor arguments:
* - 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.
* - 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.
*/
[Throws]
constructor(sequence<DOMString> aResourceIds,
optional boolean aSync = false,
optional BundleGenerator aBundleGenerator = {});
constructor(optional sequence<DOMString> aResourceIds,
optional GenerateMessages aGenerateMessages);
/**
* A method for adding resources to the localization context.
@ -152,15 +137,6 @@ 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);
};
/**

View File

@ -38,8 +38,9 @@ DOMLocalization::DOMLocalization(nsIGlobalObject* aGlobal)
}
already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
@ -48,7 +49,20 @@ already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
}
RefPtr<DOMLocalization> domLoc = new DOMLocalization(global);
domLoc->Init(aResourceIds, aSync, aBundleGenerator, aRv);
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);
}
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@ -177,7 +191,7 @@ class ElementTranslationHandler : public PromiseNativeHandler {
JS::Handle<JS::Value> aValue) override {
ErrorResult rv;
nsTArray<Nullable<L10nMessage>> l10nData;
nsTArray<L10nMessage> l10nData;
if (aValue.isObject()) {
JS::ForOfIterator iter(aCx);
if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) {
@ -201,18 +215,15 @@ class ElementTranslationHandler : public PromiseNativeHandler {
break;
}
Nullable<L10nMessage>* slotPtr =
l10nData.AppendElement(mozilla::fallible);
L10nMessage* slotPtr = l10nData.AppendElement(mozilla::fallible);
if (!slotPtr) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
}
if (!temp.isNull()) {
if (!slotPtr->SetValue().Init(aCx, temp)) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
}
if (!slotPtr->Init(aCx, temp)) {
mReturnValuePromise->MaybeRejectWithUndefined();
return;
}
}
}
@ -314,11 +325,39 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
}
if (mIsSync) {
nsTArray<Nullable<L10nMessage>> l10nMessages;
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);
}
FormatMessagesSync(cx, l10nKeys, l10nMessages, aRv);
nsTArray<JS::Value> messages;
SequenceRooter<JS::Value> messagesRooter(cx, &messages);
mLocalization->FormatMessagesSync(jsKeys, messages);
nsTArray<L10nMessage> l10nData;
SequenceRooter<L10nMessage> l10nDataRooter(cx, &l10nData);
ApplyTranslations(domElements, l10nMessages, aProto, aRv);
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);
}
if (!slotPtr->Init(cx, rootedMsg)) {
promise->MaybeRejectWithUndefined();
return MaybeWrapPromise(promise);
}
}
ApplyTranslations(domElements, l10nData, aProto, aRv);
if (NS_WARN_IF(aRv.Failed())) {
promise->MaybeRejectWithUndefined();
return MaybeWrapPromise(promise);
@ -442,10 +481,10 @@ void DOMLocalization::SetRootInfo(Element* aElement) {
aElement->SetAttr(kNameSpaceID_None, dirAtom, dir, true);
}
void DOMLocalization::ApplyTranslations(
nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<Nullable<L10nMessage>>& aTranslations,
nsXULPrototypeDocument* aProto, ErrorResult& aRv) {
void DOMLocalization::ApplyTranslations(nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<L10nMessage>& aTranslations,
nsXULPrototypeDocument* aProto,
ErrorResult& aRv) {
if (aElements.Length() != aTranslations.Length()) {
aRv.Throw(NS_ERROR_FAILURE);
return;
@ -460,11 +499,7 @@ void DOMLocalization::ApplyTranslations(
nsTArray<L10nOverlaysError> errors;
for (size_t i = 0; i < aTranslations.Length(); ++i) {
Element* elem = aElements[i];
if (aTranslations[i].IsNull()) {
continue;
}
L10nOverlays::TranslateElement(*elem, aTranslations[i].Value(), errors,
aRv);
L10nOverlays::TranslateElement(*elem, aTranslations[i], errors, aRv);
if (NS_WARN_IF(aRv.Failed())) {
aRv.Throw(NS_ERROR_FAILURE);
return;
@ -472,8 +507,7 @@ void DOMLocalization::ApplyTranslations(
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].Value().mValue.IsVoid());
aProto->RebuildL10nPrototype(elem, !aTranslations[i].mValue.IsVoid());
}
}

View File

@ -26,8 +26,9 @@ class DOMLocalization : public intl::Localization {
explicit DOMLocalization(nsIGlobalObject* aGlobal);
static already_AddRefed<DOMLocalization> Constructor(
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx,
@ -86,7 +87,7 @@ class DOMLocalization : public intl::Localization {
* the localized elements.
*/
void ApplyTranslations(nsTArray<nsCOMPtr<Element>>& aElements,
nsTArray<Nullable<L10nMessage>>& aTranslations,
nsTArray<L10nMessage>& aTranslations,
nsXULPrototypeDocument* aProto, ErrorResult& aRv);
bool SubtreeRootInRoots(nsINode* aSubtreeRoot) {

View File

@ -41,12 +41,11 @@ DocumentL10n::DocumentL10n(Document* aDocument)
}
}
void DocumentL10n::Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv) {
DOMLocalization::Init(aResourceIds, mIsSync, {}, aRv);
void DocumentL10n::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
DOMLocalization::Init(aResourceIds, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
mReady = Promise::Create(mGlobal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;

View File

@ -35,7 +35,7 @@ class DocumentL10n final : public DOMLocalization {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentL10n, DOMLocalization)
explicit DocumentL10n(Document* aDocument);
void Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv);
void Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv);
protected:
virtual ~DocumentL10n() = default;

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Value for Key 1
@ -21,8 +21,7 @@ key2 = Value for <a>Key 2<a/>.
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body);

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Value for Key 1
@ -22,8 +22,7 @@ key1 = Value for Key 1
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateRoots();

View File

@ -44,7 +44,7 @@
</script>
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Value for Key 1
@ -54,8 +54,7 @@ key1 = Value for Key 1
document.domLoc = new DOMLocalization(
[],
false,
{ generateBundles }
mockGenerateMessages
);
</script>
</head>

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Value for Key 1
@ -23,8 +23,7 @@ key2 = Value for Key 2
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateRoots();

View File

@ -10,7 +10,7 @@
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript">
<![CDATA[
async function* generateBundles(resourceIds) {
async function * generateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
file-menu =
@ -29,8 +29,7 @@ container = Some text with an <image data-l10n-name="foo"> inside it.
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
generateMessages
);
async function foo() {

View File

@ -8,15 +8,14 @@
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {}
async function* mockGenerateMessages(resourceIds) {}
window.onload = function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const p1 = document.querySelectorAll("p")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource("title = Hello World"));
bundle.addResource(new FluentResource("title2 = Hello Another World"));
@ -19,8 +19,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const h1 = document.querySelectorAll("h1")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource("title = <strong>Hello</strong> World"));
bundle.addResource(new FluentResource(`title2 = This is <a data-l10n-name="link">a link</a>!`));
@ -19,8 +19,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const p1 = document.querySelectorAll("p")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
// No translations!
yield bundle;
@ -17,8 +17,7 @@
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body).then(() => {

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`));
yield bundle;
@ -18,8 +18,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body);

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`));
yield bundle;
@ -18,8 +18,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body);

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 =
@ -22,8 +22,7 @@ key2 =
async function test() {
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body);

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Translation For Key 1
@ -21,8 +21,7 @@ key2 = Visit <a data-l10n-name="link">this link<a/>.
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
await domLoc.translateFragment(document.body);

View File

@ -8,15 +8,14 @@
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {}
async function* mockGenerateMessages(resourceIds) {}
window.onload = function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const p1 = document.querySelectorAll("p")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource("title = Hello World"));
bundle.addResource(new FluentResource("link =\n .title = Click me"));
@ -19,8 +19,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const p1 = document.querySelectorAll("p")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource("title = Hello World"));
bundle.addResource(new FluentResource("subtitle = Welcome to FluentBundle"));
@ -19,8 +19,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const frag = document.querySelectorAll("div")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource("title = Hello World"));
bundle.addResource(new FluentResource("title2 = Hello Another World"));
@ -19,8 +19,7 @@
const domLoc = new DOMLocalization(
[],
false,
{ generateBundles },
mockGenerateMessages
);
const frag1 = document.querySelectorAll("div")[0];

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US");
bundle.addResource(new FluentResource(`
key1 = Key 1
@ -20,8 +20,7 @@ key4 = Key 4
document.domLoc = new DOMLocalization(
[],
false,
{ generateBundles }
mockGenerateMessages
);
document.domLoc.connectRoot(document.documentElement);
</script>

View File

@ -45,42 +45,36 @@ NS_INTERFACE_MAP_END
Localization::Localization(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal), mIsSync(false) {}
void Localization::Init(const Sequence<nsString>& aResourceIds,
const bool aSync,
const BundleGenerator& aBundleGenerator,
void Localization::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
nsCOMPtr<mozILocalizationJSM> jsm =
do_ImportModule("resource://gre/modules/Localization.jsm");
MOZ_RELEASE_ASSERT(jsm);
Unused << jsm->GetLocalization(aResourceIds, mIsSync,
getter_AddRefs(mLocalization));
MOZ_RELEASE_ASSERT(mLocalization);
RegisterObservers();
}
void Localization::Init(nsTArray<nsString>& aResourceIds,
JS::Handle<JS::Value> aGenerateMessages,
ErrorResult& aRv) {
nsCOMPtr<mozILocalizationJSM> jsm =
do_ImportModule("resource://gre/modules/Localization.jsm");
MOZ_RELEASE_ASSERT(jsm);
Unused << jsm->GetLocalization(aResourceIds, getter_AddRefs(mLocalization));
Unused << jsm->GetLocalizationWithCustomGenerateMessages(
aResourceIds, aGenerateMessages, getter_AddRefs(mLocalization));
MOZ_RELEASE_ASSERT(mLocalization);
mLocalization->SetIsSync(aSync);
AutoJSContext cx;
if (aBundleGenerator.mGenerateBundles.WasPassed()) {
GenerateBundles& generateBundles =
aBundleGenerator.mGenerateBundles.Value();
JS::Rooted<JS::Value> generateBundlesJS(
cx, JS::ObjectValue(*generateBundles.CallbackOrNull()));
mLocalization->SetGenerateBundles(generateBundlesJS);
}
if (aBundleGenerator.mGenerateBundlesSync.WasPassed()) {
GenerateBundlesSync& generateBundlesSync =
aBundleGenerator.mGenerateBundlesSync.Value();
JS::Rooted<JS::Value> generateBundlesSyncJS(
cx, JS::ObjectValue(*generateBundlesSync.CallbackOrNull()));
mLocalization->SetGenerateBundlesSync(generateBundlesSyncJS);
}
mLocalization->Init(true);
RegisterObservers();
}
already_AddRefed<Localization> Localization::Constructor(
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
@ -89,8 +83,20 @@ already_AddRefed<Localization> Localization::Constructor(
}
RefPtr<Localization> loc = new Localization(global);
nsTArray<nsString> resourceIds;
loc->Init(aResourceIds, aSync, aBundleGenerator, aRv);
if (aResourceIds.WasPassed()) {
resourceIds = aResourceIds.Value();
}
if (aGenerateMessages.WasPassed()) {
GenerateMessages& generateMessages = aGenerateMessages.Value();
JS::Rooted<JS::Value> generateMessagesJS(
aGlobal.Context(), JS::ObjectValue(*generateMessages.CallbackOrNull()));
loc->Init(resourceIds, generateMessagesJS, aRv);
} else {
loc->Init(resourceIds, aRv);
}
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@ -146,7 +152,7 @@ Localization::Observe(nsISupports* aSubject, const char* aTopic,
void Localization::OnChange() {
if (mLocalization) {
mLocalization->OnChange(false);
mLocalization->OnChange();
}
}
@ -197,10 +203,6 @@ already_AddRefed<Promise> Localization::FormatValue(
return MaybeWrapPromise(promise);
}
void Localization::SetIsSync(const bool aIsSync) {
mLocalization->SetIsSync(aIsSync);
}
already_AddRefed<Promise> Localization::FormatValues(
JSContext* aCx, const Sequence<L10nKey>& aKeys, ErrorResult& aRv) {
nsTArray<JS::Value> jsKeys;
@ -245,81 +247,6 @@ already_AddRefed<Promise> Localization::FormatMessages(
return MaybeWrapPromise(promise);
}
void Localization::FormatValueSync(JSContext* aCx, const nsACString& aId,
const Optional<L10nArgs>& aArgs,
nsACString& aRetVal, ErrorResult& aRv) {
JS::Rooted<JS::Value> args(aCx);
if (aArgs.WasPassed()) {
ConvertL10nArgsToJSValue(aCx, aArgs.Value(), &args, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
} else {
args = JS::UndefinedValue();
}
aRv = mLocalization->FormatValueSync(aId, args, aRetVal);
}
void Localization::FormatValuesSync(JSContext* aCx,
const Sequence<L10nKey>& aKeys,
nsTArray<nsCString>& aRetVal,
ErrorResult& aRv) {
nsTArray<JS::Value> jsKeys;
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
for (auto& key : aKeys) {
JS::RootedValue jsKey(aCx);
if (!ToJSValue(aCx, key, &jsKey)) {
aRv.NoteJSContextException(aCx);
return;
}
jsKeys.AppendElement(jsKey);
}
aRv = mLocalization->FormatValuesSync(jsKeys, aRetVal);
}
void Localization::FormatMessagesSync(JSContext* aCx,
const Sequence<L10nKey>& aKeys,
nsTArray<Nullable<L10nMessage>>& aRetVal,
ErrorResult& aRv) {
nsTArray<JS::Value> jsKeys;
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
for (auto& key : aKeys) {
JS::RootedValue jsKey(aCx);
if (!ToJSValue(aCx, key, &jsKey)) {
aRv.NoteJSContextException(aCx);
return;
}
jsKeys.AppendElement(jsKey);
}
nsTArray<JS::Value> messages;
SequenceRooter<JS::Value> messagesRooter(aCx, &messages);
aRv = mLocalization->FormatMessagesSync(jsKeys, messages);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
for (auto& msg : messages) {
JS::Rooted<JS::Value> rootedMsg(aCx);
rootedMsg.set(msg);
Nullable<L10nMessage>* slotPtr = aRetVal.AppendElement(mozilla::fallible);
if (!slotPtr) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
if (!msg.isNull()) {
if (!slotPtr->SetValue().Init(aCx, rootedMsg)) {
aRv.NoteJSContextException(aCx);
return;
}
}
}
}
/**
* PromiseResolver is a PromiseNativeHandler used
* by MaybeWrapPromise method.

View File

@ -33,12 +33,14 @@ class Localization : public nsIObserver,
NS_DECL_NSIOBSERVER
explicit Localization(nsIGlobalObject* aGlobal);
void Init(const Sequence<nsString>& aResourceIds, const bool aSync,
const BundleGenerator& aBundleGenerator, ErrorResult& aRv);
void Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv);
void Init(nsTArray<nsString>& aResourceIds,
JS::Handle<JS::Value> aGenerateMessages, ErrorResult& aRv);
static already_AddRefed<Localization> Constructor(
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
const bool aSync, const BundleGenerator& aBundleGenerator,
const GlobalObject& aGlobal,
const Optional<Sequence<nsString>>& aResourceIds,
const Optional<OwningNonNull<GenerateMessages>>& aGenerateMessages,
ErrorResult& aRv);
nsIGlobalObject* GetParentObject() const;
@ -67,17 +69,6 @@ class Localization : public nsIObserver,
const Sequence<L10nKey>& aKeys,
ErrorResult& aRv);
void SetIsSync(const bool aIsSync);
void FormatValueSync(JSContext* aCx, const nsACString& aId,
const Optional<L10nArgs>& aArgs, nsACString& aRetVal,
ErrorResult& aRv);
void FormatValuesSync(JSContext* aCx, const Sequence<L10nKey>& aKeys,
nsTArray<nsCString>& aRetVal, ErrorResult& aRv);
void FormatMessagesSync(JSContext* aCx, const Sequence<L10nKey>& aKeys,
nsTArray<Nullable<L10nMessage>>& aRetVal,
ErrorResult& aRv);
protected:
virtual ~Localization();
void RegisterObservers();

View File

@ -212,29 +212,19 @@ function maybeReportErrorToGecko(error) {
class Localization {
/**
* @param {Array<String>} resourceIds - List of resource IDs
* @param {Function} generateBundles - Function that returns an async
* generator over FluentBundles
* @param {Function} generateBundlesSync - Function that returns a sync
* generator over FluentBundles
*
* @returns {Localization}
*/
constructor(resourceIds = []) {
constructor(resourceIds = [], sync = false, generateBundles = defaultGenerateBundles, generateBundlesSync = defaultGenerateBundlesSync) {
this.isSync = sync;
this.resourceIds = resourceIds;
this.generateBundles = defaultGenerateBundles;
this.generateBundlesSync = defaultGenerateBundlesSync;
}
setGenerateBundles(generateBundles) {
this.generateBundles = generateBundles;
}
setGenerateBundlesSync(generateBundlesSync) {
this.generateBundlesSync = generateBundlesSync;
}
setIsSync(isSync) {
this.isSync = isSync;
}
init(eager = false) {
this.onChange(eager);
this.onChange(true);
}
cached(iterable) {
@ -510,6 +500,11 @@ class Localization {
this.bundles.touchNext(prefetchCount);
}
}
setIsSync(isSync) {
this.isSync = isSync;
this.onChange();
}
}
Localization.prototype.QueryInterface = ChromeUtils.generateQI([
@ -639,9 +634,13 @@ function keysFromBundle(method, bundle, keys, translations) {
* Helper function which allows us to construct a new
* Localization from Localization.
*/
var getLocalization = (resourceIds) => {
return new Localization(resourceIds);
var getLocalization = (resourceIds, sync = false) => {
return new Localization(resourceIds, sync);
};
var getLocalizationWithCustomGenerateMessages = (resourceIds, generateMessages) => {
return new Localization(resourceIds, false, generateMessages);
};
this.Localization = Localization;
var EXPORTED_SYMBOLS = ["Localization", "getLocalization"];
var EXPORTED_SYMBOLS = ["Localization", "getLocalization", "getLocalizationWithCustomGenerateMessages"];

View File

@ -14,28 +14,21 @@
[scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)]
interface mozILocalization : nsISupports
{
void setGenerateBundles(in jsval generateBundles);
void setGenerateBundlesSync(in jsval generateBundlesSync);
void setIsSync(in boolean isSync);
void init(in boolean aEager);
unsigned long addResourceIds(in Array<AString> resourceIds, in bool aEager);
unsigned long removeResourceIds(in Array<AString> resourceIds);
void onChange();
Promise formatMessages(in Array<jsval> aKeys);
Promise formatValues(in Array<jsval> aKeys);
Promise formatValue(in AUTF8String aId, [optional] in jsval aArgs);
AUTF8String formatValueSync(in AUTF8String aId, [optional] in jsval aArgs);
Array<AUTF8String> formatValuesSync(in Array<jsval> aKeys);
Array<jsval> formatMessagesSync(in Array<jsval> aKeys);
void onChange(in boolean aEager);
void setIsSync(in boolean isSync);
};
[scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)]
interface mozILocalizationJSM : nsISupports
{
mozILocalization getLocalization(in Array<AString> resourceIds);
mozILocalization getLocalization(in Array<AString> resourceIds, in bool sync);
mozILocalization getLocalizationWithCustomGenerateMessages(in Array<AString> resourceIds, in jsval generateMessages);
};

View File

@ -8,7 +8,7 @@
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US", {
useIsolating: false,
});
@ -31,8 +31,7 @@ key2 =
const loc = new Localization(
['mock.ftl'],
false,
{ generateBundles },
mockGenerateMessages,
);
{

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US", {
useIsolating: false,
});
@ -24,8 +24,7 @@ key3 = Value { $count }
const loc = new Localization(
['mock.ftl'],
false,
{ generateBundles },
mockGenerateMessages,
);
{

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript">
"use strict";
async function* generateBundles(resourceIds) {
async function* mockGenerateMessages(resourceIds) {
const bundle = new FluentBundle("en-US",
{
useIsolating: false,
@ -25,8 +25,7 @@ key3 = Value { $count }
const loc = new Localization(
['mock.ftl'],
false,
{ generateBundles },
mockGenerateMessages,
);
{

View File

@ -2,6 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(function test_methods_presence() {
@ -28,13 +29,13 @@ add_task(async function test_methods_calling() {
const source = new FileSource("test", ["de", "en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateBundles(resIds) {
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateBundles(["de", "en-US"], resIds);
}
const l10n = new Localization([
"/browser/menu.ftl",
], false, { generateBundles });
], false, generateMessages);
let values = await l10n.formatValues([{id: "key"}, {id: "key2"}]);
@ -74,13 +75,13 @@ key = { PLATFORM() ->
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateBundles(resIds) {
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
}
const l10n = new Localization([
"/test.ftl",
], false, { generateBundles });
], false, generateMessages);
let values = await l10n.formatValues([{id: "key"}]);
@ -109,11 +110,11 @@ add_task(async function test_add_remove_resourceIds() {
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateBundles(resIds) {
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
}
const l10n = new Localization(["/browser/menu.ftl"], false, { generateBundles });
const l10n = new Localization(["/browser/menu.ftl"], false, generateMessages);
let values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
@ -167,15 +168,15 @@ add_task(async function test_switch_to_async() {
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateBundles(resIds) {
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
}
function* generateBundlesSync(resIds) {
function* generateMessagesSync(resIds) {
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
}
const l10n = new Localization(["/browser/menu.ftl"], false, { generateBundles, generateBundlesSync });
const l10n = new Localization(["/browser/menu.ftl"], false, generateMessages, generateMessagesSync);
let values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);

View File

@ -2,6 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(function test_methods_calling() {
@ -22,13 +23,13 @@ add_task(function test_methods_calling() {
const source = new FileSource("test", ["de", "en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
function* generateBundlesSync(resIds) {
function* generateMessagesSync(resIds) {
yield * L10nRegistry.generateBundlesSync(["de", "en-US"], resIds);
}
const l10n = new Localization([
"/browser/menu.ftl",
], true, { generateBundlesSync });
], true, null, generateMessagesSync);
let values = l10n.formatValuesSync([{id: "key"}, {id: "key2"}]);
@ -68,13 +69,13 @@ key = { PLATFORM() ->
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
function* generateBundlesSync(resIds) {
function* generateMessagesSync(resIds) {
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
}
const l10n = new Localization([
"/test.ftl",
], true, { generateBundlesSync });
], true, null, generateMessagesSync);
let values = l10n.formatValuesSync([{id: "key"}]);
@ -103,11 +104,11 @@ add_task(function test_add_remove_resourceIds() {
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
function* generateBundlesSync(resIds) {
function* generateMessagesSync(resIds) {
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
}
const l10n = new Localization(["/browser/menu.ftl"], true, { generateBundlesSync });
const l10n = new Localization(["/browser/menu.ftl"], true, null, generateMessagesSync);
let values = l10n.formatValuesSync([{id: "key1"}, {id: "key2"}]);
@ -147,4 +148,4 @@ add_task(function test_calling_sync_methods_in_async_mode_fails() {
Assert.throws(() => {
l10n.formatMessagesSync([{ id: "key1"}]);
}, /Can't use sync formatWithFallback when state is async./);
});
});

View File

@ -1,6 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { L10nRegistry, FileSource } =
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
@ -45,11 +46,12 @@ function getAttributeByName(attributes, name) {
add_task(async function test_accented_works() {
Services.prefs.setStringPref("intl.l10n.pseudo", "");
let generateBundles = addMockFileSource();
let generateMessages = addMockFileSource();
const l10n = new Localization([
"/browser/menu.ftl",
], false, { generateBundles });
], false, generateMessages);
l10n.registerObservers();
{
// 1. Start with no pseudo
@ -114,11 +116,12 @@ add_task(async function test_accented_works() {
add_task(async function test_unavailable_strategy_works() {
Services.prefs.setStringPref("intl.l10n.pseudo", "");
let generateBundles = addMockFileSource();
let generateMessages = addMockFileSource();
const l10n = new Localization([
"/browser/menu.ftl",
], false, { generateBundles });
], false, generateMessages);
l10n.registerObservers();
{
// 1. Set unavailable pseudo strategy

View File

@ -61,6 +61,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
FileSource: "resource://gre/modules/L10nRegistry.jsm",
L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
Localization: "resource://gre/modules/Localization.jsm",
Log: "resource://gre/modules/Log.jsm",
MessageChannel: "resource://gre/modules/MessageChannel.jsm",
NetUtil: "resource://gre/modules/NetUtil.jsm",

View File

@ -3,6 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Localization } = ChromeUtils.import(
"resource://gre/modules/Localization.jsm",
null
);
const mozIntlHelper = Cc["@mozilla.org/mozintlhelper;1"].getService(
Ci.mozIMozIntlHelper