Bug 1831259 - prevent initialization re-entrancy for preference panes, r=mconley

This should fix the issue experienced by the reporter. It also pushes the
re-entrancy guard into the the 'init' method we define on gCategoryInits
objects, and removes the asynchronicity which was only there for fluent's sake.

Instead of blocking the insertion on fluent, which meant that for the in-page
find functionality we do N initializations and fluent passes, we make each of
the 2 consumers responsible for checking translation has completed. This means
find in page now just has 1 fluent pass, instead of N passes for N categories.
This should speed up the find in page initialization, and means initialization
of a category is now sync instead of async.

Differential Revision: https://phabricator.services.mozilla.com/D178232
This commit is contained in:
Gijs Kruitbosch 2023-05-23 13:16:18 +00:00
Родитель 40c92c02c2
Коммит 3c8d3721dd
3 изменённых файлов: 45 добавлений и 47 удалений

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

@ -105,10 +105,13 @@ var gSearchResultsPane = {
if (!this.categoriesInitialized) {
this.categoriesInitialized = true;
// Each element of gCategoryInits is a name
for (let [, /* name */ category] of gCategoryInits) {
if (!category.inited) {
await category.init();
}
for (let category of gCategoryInits.values()) {
category.init();
}
if (document.hasPendingL10nMutations) {
await new Promise(r =>
document.addEventListener("L10nMutationsFinished", r, { once: true })
);
}
}
},

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

@ -155,43 +155,24 @@ var gLastCategory = { category: undefined, subcategory: undefined };
const gXULDOMParser = new DOMParser();
var gCategoryModules = new Map();
var gCategoryInits = new Map();
function init_category_if_required(category) {
let categoryInfo = gCategoryInits.get(category);
if (!categoryInfo) {
throw new Error(
"Unknown in-content prefs category! Can't init " + category
);
}
if (categoryInfo.inited) {
return null;
}
return categoryInfo.init();
}
function register_module(categoryName, categoryObject) {
gCategoryModules.set(categoryName, categoryObject);
gCategoryInits.set(categoryName, {
inited: false,
async init() {
_initted: false,
init() {
let startTime = performance.now();
if (this._initted) {
return;
}
this._initted = true;
let template = document.getElementById("template-" + categoryName);
if (template) {
// Replace the template element with the nodes inside of it.
let frag = template.content;
await document.l10n.translateFragment(frag);
// Actually insert them into the DOM.
document.l10n.pauseObserving();
template.replaceWith(frag);
document.l10n.resumeObserving();
// We need to queue an update again because the previous update might
// have happened while we awaited on translateFragment.
Preferences.queueUpdateOfAllElements();
template.replaceWith(template.content);
}
categoryObject.init();
this.inited = true;
ChromeUtils.addProfilerMarker(
"Preferences",
{ startTime },
@ -386,24 +367,28 @@ async function gotoPref(
}
window.history.replaceState(category, document.title);
try {
await init_category_if_required(category);
} catch (ex) {
console.error(
new Error(
"Error initializing preference category " + category + ": " + ex
)
let categoryInfo = gCategoryInits.get(category);
if (!categoryInfo) {
let err = new Error(
"Unknown in-content prefs category! Can't init " + category
);
throw ex;
console.error(err);
throw err;
}
categoryInfo.init();
// Bail out of this goToPref if the category
// or subcategory changed during async operation.
if (
gLastCategory.category !== category ||
gLastCategory.subcategory !== subcategory
) {
return;
if (document.hasPendingL10nMutations) {
await new Promise(r =>
document.addEventListener("L10nMutationsFinished", r, { once: true })
);
// Bail out of this goToPref if the category
// or subcategory changed during async operation.
if (
gLastCategory.category !== category ||
gLastCategory.subcategory !== subcategory
) {
return;
}
}
search(category, "data-category");

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

@ -143,17 +143,27 @@ add_task(async function testFilterFeatures() {
);
}
// Check that switching to a non-find-in-page category changes item
// visibility appropriately.
EventUtils.synthesizeMouseAtCenter(
doc.getElementById(category),
{},
gBrowser.contentWindow
);
// Ensure that async passes of localization and any code waiting for
// those passes have finished running.
await new Promise(r =>
requestAnimationFrame(() => requestAnimationFrame(r))
);
let shouldShow = category == "category-experimental";
for (let definition of definitions) {
checkVisibility(
doc.getElementById(definition.id),
true,
`${definition.id} should be visible after category change to ${category}`
shouldShow,
`${definition.id} should be ${
shouldShow ? "visible" : "hidden"
} after category change to ${category}`
);
}
}