Bug 1532712 - Optimize Preferences translation. r=gijs
authorZibi Braniecki <zbraniecki@mozilla.com>
Wed, 13 Mar 2019 00:27:31 +0000
changeset 522069 10dfa9788b2b83e2a56a51d53cfe75813f89fa2e
parent 522068 9e076e178370e309828ca0b2a598058df83848af
child 522070 adbb2fb5c8eca65f1df0ca77b20933c9aae54ae7
push id10871
push usercbrindusan@mozilla.com
push dateMon, 18 Mar 2019 15:49:32 +0000
treeherdermozilla-beta@018abdd16060 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs
bugs1532712
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1532712 - Optimize Preferences translation. r=gijs Differential Revision: https://phabricator.services.mozilla.com//D23245
browser/components/preferences/in-content/preferences.js
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -19,83 +19,62 @@
 
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AMTelemetry",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "formAutofillParent",
                                "resource://formautofill/FormAutofillParent.jsm");
 
-var gLastHash = "";
+var gLastCategory = {category: undefined, subcategory: undefined};
 const gXULDOMParser = new DOMParser();
 
 var gCategoryInits = new Map();
 function init_category_if_required(category) {
   let categoryInfo = gCategoryInits.get(category);
   if (!categoryInfo) {
     throw "Unknown in-content prefs category! Can't init " + category;
   }
   if (categoryInfo.inited) {
-    return;
+    return null;
   }
-  categoryInfo.init();
+  return categoryInfo.init();
 }
 
 function register_module(categoryName, categoryObject) {
   gCategoryInits.set(categoryName, {
     inited: false,
-    init() {
+    async init() {
       let template = document.getElementById("template-" + categoryName);
       if (template) {
         // Replace the template element with the nodes from the parsed comment
         // string.
         let frag = MozXULElement.parseXULToFragment(template.firstChild.data);
 
-        // Gather the to-be-translated elements so that we could pass them to
-        // l10n.translateElements() and get a translated promise.
-        // Here we loop through the first level elements (<hbox>/<groupbox>/<deck>/etc)
-        // because we know that they are not implemented by XBL bindings,
-        // so it's ok to get a reference of them before inserting the node
-        // to the DOM.
-        //
-        // If we don't have to worry about XBL, this can simply be
-        // let l10nUpdatedElements = Array.from(frag.querySelectorAll("[data-l10n-id]"))
-        //
-        // If we can get a translated promise after insertion, this can all be
-        // removed (see bug 1520659.)
-        let firstLevelElements = Array.from(frag.children);
+        await document.l10n.translateFragment(frag);
 
         // Actually insert them into the DOM.
+        document.l10n.pauseObserving();
         template.replaceWith(frag);
-
-        let l10nUpdatedElements = [];
-        // Collect the elements from the newly inserted first level elements.
-        for (let el of firstLevelElements) {
-          l10nUpdatedElements = l10nUpdatedElements.concat(
-            Array.from(el.querySelectorAll("[data-l10n-id]")));
-        }
-
-        // Set a promise on the categoryInfo object that the highlight code can await on.
-        this.translated = document.l10n.translateElements(l10nUpdatedElements)
-          .then(() => this.translated = undefined);
+        document.l10n.resumeObserving();
 
         // Asks Preferences to update the attribute value of the entire
         // document again (this can be simplified if we could seperate the
         // preferences of each pane.)
         Preferences.updateAllElements();
       }
       categoryObject.init();
       this.inited = true;
     },
   });
 }
 
 document.addEventListener("DOMContentLoaded", init_all, {once: true});
 
-function init_all() {
+async function init_all() {
   Preferences.forceEnableInstantApply();
 
   gSubDialog.init();
   register_module("paneGeneral", gMainPane);
   register_module("paneHome", gHomePane);
   register_module("paneSearch", gSearchPane);
   register_module("panePrivacy", gPrivacyPane);
   register_module("paneContainers", gContainersPane);
@@ -117,17 +96,17 @@ function init_all() {
   });
   categories.addEventListener("mousedown", function() {
     this.removeAttribute("keyboard-navigation");
   });
 
   maybeDisplayPoliciesNotice();
 
   window.addEventListener("hashchange", onHashChange);
-  gotoPref();
+  await gotoPref();
 
   let helpButton = document.getElementById("helpButton");
   let helpUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "preferences";
   helpButton.setAttribute("href", helpUrl);
 
   document.getElementById("addonsButton")
     .addEventListener("click", () => {
       let mainWindow = window.docShell.rootTreeItem.domWindow;
@@ -159,17 +138,17 @@ function telemetryBucketForCategory(cate
       return "unknown";
   }
 }
 
 function onHashChange() {
   gotoPref();
 }
 
-function gotoPref(aCategory) {
+async function gotoPref(aCategory) {
   let categories = document.getElementById("categories");
   const kDefaultCategoryInternalName = "paneGeneral";
   const kDefaultCategory = "general";
   let hash = document.location.hash;
 
   let category = aCategory || hash.substr(1) || kDefaultCategoryInternalName;
   let breakIndex = category.indexOf("-");
   // Subcategories allow for selecting smaller sections of the preferences
@@ -190,48 +169,57 @@ function gotoPref(aCategory) {
     // a query string. Default to the General pane instead.
     category = kDefaultCategoryInternalName;
     document.location.hash = kDefaultCategory;
     gSearchResultsPane.query = null;
   }
 
   // Updating the hash (below) or changing the selected category
   // will re-enter gotoPref.
-  if (gLastHash == category && !subcategory)
+  if (gLastCategory.category == category && !subcategory)
     return;
 
   let item;
   if (category != "paneSearchResults") {
     item = categories.querySelector(".category[value=" + category + "]");
     if (!item) {
       category = kDefaultCategoryInternalName;
       item = categories.querySelector(".category[value=" + category + "]");
     }
   }
 
-  try {
-    init_category_if_required(category);
-  } catch (ex) {
-    Cu.reportError("Error initializing preference category " + category + ": " + ex);
-    throw ex;
-  }
-
-  let friendlyName = internalPrefCategoryNameToFriendlyName(category);
-  if (gLastHash || category != kDefaultCategoryInternalName || subcategory) {
+  if (gLastCategory.category || category != kDefaultCategoryInternalName || subcategory) {
+    let friendlyName = internalPrefCategoryNameToFriendlyName(category);
     document.location.hash = friendlyName;
   }
-  // Need to set the gLastHash before setting categories.selectedItem since
+  // Need to set the gLastCategory before setting categories.selectedItem since
   // the categories 'select' event will re-enter the gotoPref codepath.
-  gLastHash = category;
+  gLastCategory.category = category;
+  gLastCategory.subcategory = subcategory;
   if (item) {
     categories.selectedItem = item;
   } else {
     categories.clearSelection();
   }
   window.history.replaceState(category, document.title);
+
+  try {
+    await init_category_if_required(category);
+  } catch (ex) {
+    Cu.reportError(new Error("Error initializing preference category " + category + ": " + ex));
+    throw ex;
+  }
+
+  // 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");
 
   let mainContent = document.querySelector(".main-content");
   mainContent.scrollTop = 0;
 
   spotlight(subcategory, category);
 }
 
@@ -279,17 +267,16 @@ async function spotlight(subcategory, ca
 }
 
 async function scrollAndHighlight(subcategory, category) {
   let element = document.querySelector(`[data-subcategory="${subcategory}"]`);
   if (!element) {
     return;
   }
   let header = getClosestDisplayedHeader(element);
-  await gCategoryInits.get(category).translated;
 
   scrollContentTo(header);
   element.classList.add("spotlight");
 }
 
 /**
  * If there is no visible second level header it will return first level header,
  * otherwise return second level header.