зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1520350 - Lazily load about:preferences markups from hidden panes r=jaws
Because custom elements will be constructed when DOM is constructed, construct the DOM in the hidden panels will be expensive as we move more and more widgets to custom elements from XBL. This patch attempts to counter that by moving all the pane markups into comment nodes, and use MozXULElement.parseXULToFragment() to insert it when it is being asked. They will be loaded lazily from an requestIdleCallback() in findInPage.js. Differential Revision: https://phabricator.services.mozilla.com/D16787 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1ef7b54002
Коммит
de9b8ba1e2
|
@ -24,7 +24,7 @@ registerCleanupFunction(async function cleanup_prefs() {
|
|||
});
|
||||
|
||||
async function test_popup_blocker_disabled({disabled, locked}) {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences#privacy");
|
||||
// eslint-disable-next-line no-shadow
|
||||
await ContentTask.spawn(tab.linkedBrowser, {disabled, locked}, async function({disabled, locked}) {
|
||||
let checkbox = content.document.getElementById("popupPolicy");
|
||||
|
|
|
@ -135,7 +135,7 @@ add_task(async function setup_prevent_installs() {
|
|||
add_task(async function test_prevent_install_ui() {
|
||||
// Check that about:preferences does not prompt user to install search engines
|
||||
// if that feature is disabled
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences#search");
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
|
||||
let linkContainer = content.document.getElementById("addEnginesBox");
|
||||
if (!linkContainer.hidden) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/home.js"/>
|
||||
|
||||
<box id="template-paneHome" hidden="true"><![CDATA[
|
||||
<hbox id="firefoxHomeCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
@ -98,3 +98,4 @@
|
|||
data-l10n-id="disable-extension" />
|
||||
</hbox>
|
||||
</groupbox>
|
||||
]]></box>
|
||||
|
|
|
@ -399,6 +399,20 @@ var gMainPane = {
|
|||
}
|
||||
}
|
||||
|
||||
let drmInfoURL =
|
||||
Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
|
||||
let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
|
||||
// Force-disable/hide on WinXP:
|
||||
if (navigator.platform.toLowerCase().startsWith("win")) {
|
||||
emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
|
||||
}
|
||||
if (!emeUIEnabled) {
|
||||
// Don't want to rely on .hidden for the toplevel groupbox because
|
||||
// of the pane hiding/showing code potentially interfering:
|
||||
document.getElementById("drmGroup").setAttribute("style", "display: none !important");
|
||||
}
|
||||
|
||||
if (AppConstants.MOZ_DEV_EDITION) {
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
@ -477,7 +491,13 @@ var gMainPane = {
|
|||
}
|
||||
|
||||
if (AppConstants.MOZ_UPDATER) {
|
||||
gAppUpdater = new appUpdater();
|
||||
// XXX Workaround bug 1523453 -- changing selectIndex of a <deck> before
|
||||
// frame construction could confuse nsDeckFrame::RemoveFrame().
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
gAppUpdater = new appUpdater();
|
||||
});
|
||||
});
|
||||
setEventListener("showUpdateHistory", "command",
|
||||
gMainPane.showUpdates);
|
||||
|
||||
|
@ -572,6 +592,8 @@ var gMainPane = {
|
|||
|
||||
// Notify observers that the UI is now ready
|
||||
Services.obs.notifyObservers(window, "main-pane-loaded");
|
||||
|
||||
this.setInitialized();
|
||||
},
|
||||
|
||||
preInit() {
|
||||
|
@ -580,6 +602,7 @@ var gMainPane = {
|
|||
// By doing this after pageshow, we ensure it doesn't delay painting
|
||||
// of the preferences page.
|
||||
window.addEventListener("pageshow", async () => {
|
||||
await this.initialized;
|
||||
try {
|
||||
this._initListEventHandlers();
|
||||
this._loadData();
|
||||
|
@ -2484,6 +2507,10 @@ var gMainPane = {
|
|||
},
|
||||
};
|
||||
|
||||
gMainPane.initialized = new Promise(res => {
|
||||
gMainPane.setInitialized = res;
|
||||
});
|
||||
|
||||
// Utilities
|
||||
|
||||
function getFileDisplayName(file) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences.properties"/>
|
||||
|
||||
<box id="template-paneGeneral" hidden="true"><![CDATA[
|
||||
<hbox id="generalCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
@ -712,3 +713,4 @@
|
|||
</hbox>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
]]></box>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
/* import-globals-from findInPage.js */
|
||||
/* import-globals-from ../../../base/content/utilityOverlay.js */
|
||||
/* import-globals-from ../../../../toolkit/content/preferencesBindings.js */
|
||||
/* global MozXULElement */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -23,6 +24,7 @@ ChromeUtils.defineModuleGetter(this, "formAutofillParent",
|
|||
"resource://formautofill/FormAutofillParent.jsm");
|
||||
|
||||
var gLastHash = "";
|
||||
const gXULDOMParser = new DOMParser();
|
||||
|
||||
var gCategoryInits = new Map();
|
||||
function init_category_if_required(category) {
|
||||
|
@ -40,6 +42,45 @@ function register_module(categoryName, categoryObject) {
|
|||
gCategoryInits.set(categoryName, {
|
||||
inited: false,
|
||||
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);
|
||||
|
||||
// Actually insert them into the DOM.
|
||||
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);
|
||||
|
||||
// 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;
|
||||
},
|
||||
|
@ -59,7 +100,6 @@ function init_all() {
|
|||
register_module("paneContainers", gContainersPane);
|
||||
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
|
||||
document.getElementById("category-sync").hidden = false;
|
||||
document.getElementById("weavePrefsDeck").removeAttribute("data-hidden-from-search");
|
||||
register_module("paneSync", gSyncPane);
|
||||
}
|
||||
register_module("paneSearchResults", gSearchResultsPane);
|
||||
|
@ -187,7 +227,7 @@ function gotoPref(aCategory) {
|
|||
let mainContent = document.querySelector(".main-content");
|
||||
mainContent.scrollTop = 0;
|
||||
|
||||
spotlight(subcategory);
|
||||
spotlight(subcategory, category);
|
||||
}
|
||||
|
||||
function search(aQuery, aAttribute) {
|
||||
|
@ -221,7 +261,7 @@ function search(aQuery, aAttribute) {
|
|||
}
|
||||
}
|
||||
|
||||
async function spotlight(subcategory) {
|
||||
async function spotlight(subcategory, category) {
|
||||
let highlightedElements = document.querySelectorAll(".spotlight");
|
||||
if (highlightedElements.length) {
|
||||
for (let element of highlightedElements) {
|
||||
|
@ -229,71 +269,20 @@ async function spotlight(subcategory) {
|
|||
}
|
||||
}
|
||||
if (subcategory) {
|
||||
if (!gSearchResultsPane.categoriesInitialized) {
|
||||
await waitForSystemAddonInjectionsFinished([{
|
||||
isGoingToInject: formAutofillParent.initialized,
|
||||
elementId: "formAutofillGroup",
|
||||
}]);
|
||||
}
|
||||
scrollAndHighlight(subcategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for system addons finished their dom injections.
|
||||
* @param {Array} addons - The system addon information array.
|
||||
* For example, the element is looked like
|
||||
* { isGoingToInject: true, elementId: "formAutofillGroup" }.
|
||||
* The `isGoingToInject` means the system addon will be visible or not,
|
||||
* and the `elementId` means the id of the element will be injected into the dom
|
||||
* if the `isGoingToInject` is true.
|
||||
* @returns {Promise} Will resolve once all injections are finished.
|
||||
*/
|
||||
function waitForSystemAddonInjectionsFinished(addons) {
|
||||
return new Promise(resolve => {
|
||||
let elementIdSet = new Set();
|
||||
for (let addon of addons) {
|
||||
if (addon.isGoingToInject) {
|
||||
elementIdSet.add(addon.elementId);
|
||||
}
|
||||
}
|
||||
if (elementIdSet.size) {
|
||||
let observer = new MutationObserver(mutations => {
|
||||
for (let mutation of mutations) {
|
||||
for (let node of mutation.addedNodes) {
|
||||
elementIdSet.delete(node.id);
|
||||
if (elementIdSet.size === 0) {
|
||||
observer.disconnect();
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let mainContent = document.querySelector(".main-content");
|
||||
observer.observe(mainContent, {childList: true, subtree: true});
|
||||
// Disconnect the mutation observer once there is any user input.
|
||||
mainContent.addEventListener("scroll", disconnectMutationObserver);
|
||||
window.addEventListener("mousedown", disconnectMutationObserver);
|
||||
window.addEventListener("keydown", disconnectMutationObserver);
|
||||
function disconnectMutationObserver() {
|
||||
mainContent.removeEventListener("scroll", disconnectMutationObserver);
|
||||
window.removeEventListener("mousedown", disconnectMutationObserver);
|
||||
window.removeEventListener("keydown", disconnectMutationObserver);
|
||||
observer.disconnect();
|
||||
}
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
scrollAndHighlight(subcategory, category);
|
||||
}
|
||||
}
|
||||
|
||||
function scrollAndHighlight(subcategory) {
|
||||
async function scrollAndHighlight(subcategory, category) {
|
||||
let element = document.querySelector(`[data-subcategory="${subcategory}"]`);
|
||||
if (element) {
|
||||
let header = getClosestDisplayedHeader(element);
|
||||
scrollContentTo(header);
|
||||
element.classList.add("spotlight");
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
let header = getClosestDisplayedHeader(element);
|
||||
await gCategoryInits.get(category).translated;
|
||||
|
||||
scrollContentTo(header);
|
||||
element.classList.add("spotlight");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -305,8 +294,8 @@ function getClosestDisplayedHeader(element) {
|
|||
let header = element.closest("groupbox");
|
||||
let searchHeader = header.querySelector(".search-header");
|
||||
if (searchHeader && searchHeader.hidden &&
|
||||
header.previousSibling.classList.contains("subcategory")) {
|
||||
header = header.previousSibling;
|
||||
header.previousElementSibling.classList.contains("subcategory")) {
|
||||
header = header.previousElementSibling;
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
|
|
@ -387,19 +387,6 @@ var gPrivacyPane = {
|
|||
Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
|
||||
document.getElementById("notificationPermissionsLearnMore").setAttribute("href",
|
||||
notificationInfoURL);
|
||||
let drmInfoURL =
|
||||
Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
|
||||
let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
|
||||
// Force-disable/hide on WinXP:
|
||||
if (navigator.platform.toLowerCase().startsWith("win")) {
|
||||
emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
|
||||
}
|
||||
if (!emeUIEnabled) {
|
||||
// Don't want to rely on .hidden for the toplevel groupbox because
|
||||
// of the pane hiding/showing code potentially interfering:
|
||||
document.getElementById("drmGroup").setAttribute("style", "display: none !important");
|
||||
}
|
||||
|
||||
if (AppConstants.MOZ_DATA_REPORTING) {
|
||||
this.initDataCollection();
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
src="chrome://browser/content/preferences/in-content/privacy.js"/>
|
||||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
<stringbundle id="signonBundle" src="chrome://passwordmgr/locale/passwordmgr.properties"/>
|
||||
|
||||
<box id="template-panePrivacy" hidden="true"><![CDATA[
|
||||
<hbox id="browserPrivacyCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
@ -322,7 +322,7 @@
|
|||
after the form autofill extension has initialized. -->
|
||||
<groupbox id="formAutofillGroupBox"
|
||||
data-category="panePrivacy"
|
||||
data-subcategory="form-autofill"></groupbox>
|
||||
data-subcategory="form-autofill" hidden="true"></groupbox>
|
||||
|
||||
<!-- History -->
|
||||
<groupbox id="historyGroup" data-category="panePrivacy" hidden="true">
|
||||
|
@ -795,3 +795,4 @@
|
|||
</vbox>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
]]></box>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/search.js"/>
|
||||
|
||||
<box id="template-paneSearch" hidden="true"><![CDATA[
|
||||
<hbox id="searchCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
@ -8,7 +8,7 @@
|
|||
<html:h1 data-l10n-id="pane-search-title"/>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="searchbarGroup" data-category="paneSearch">
|
||||
<groupbox id="searchbarGroup" data-category="paneSearch" hidden="true">
|
||||
<label control="searchBarVisibleGroup"><html:h2 data-l10n-id="search-bar-header"/></label>
|
||||
<radiogroup id="searchBarVisibleGroup" preference="browser.search.widget.inNavBar">
|
||||
<radio id="searchBarHiddenRadio" value="false" data-l10n-id="search-bar-hidden"/>
|
||||
|
@ -19,7 +19,7 @@
|
|||
</groupbox>
|
||||
|
||||
<!-- Default Search Engine -->
|
||||
<groupbox id="defaultEngineGroup" data-category="paneSearch">
|
||||
<groupbox id="defaultEngineGroup" data-category="paneSearch" hidden="true">
|
||||
<label><html:h2 data-l10n-id="search-engine-default-header" /></label>
|
||||
<description data-l10n-id="search-engine-default-desc" />
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
|||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
<groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
|
||||
<groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch" hidden="true">
|
||||
<label><html:h2 data-l10n-id="search-one-click-header" /></label>
|
||||
<description data-l10n-id="search-one-click-desc" />
|
||||
|
||||
|
@ -81,3 +81,4 @@
|
|||
<label id="addEngines" class="text-link" data-l10n-id="search-find-more-link"></label>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
]]></box>
|
||||
|
|
|
@ -52,6 +52,8 @@ var gSyncPane = {
|
|||
this._setupEventListeners();
|
||||
this._adjustForPrefs();
|
||||
|
||||
document.getElementById("weavePrefsDeck").removeAttribute("data-hidden-from-search");
|
||||
|
||||
// If the Service hasn't finished initializing, wait for it.
|
||||
let xps = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/sync.js"/>
|
||||
|
||||
<box id="template-paneSync" hidden="true"><![CDATA[
|
||||
<hbox id="firefoxAccountCategory"
|
||||
class="subcategory"
|
||||
hidden="true"
|
||||
|
@ -196,3 +196,4 @@
|
|||
</vbox>
|
||||
</vbox>
|
||||
</deck>
|
||||
]]></box>
|
||||
|
|
|
@ -16,7 +16,7 @@ add_task(async function() {
|
|||
* it should not show the "Remove Account" button if the Firefox account is not logged in yet.
|
||||
*/
|
||||
add_task(async function() {
|
||||
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
|
||||
await openPreferencesViaOpenPreferencesAPI("paneSync", {leaveOpen: true});
|
||||
|
||||
// Ensure the "Sign Up" button in the hidden child of the <xul:deck>
|
||||
// is selected and displayed on the screen.
|
||||
|
|
|
@ -106,7 +106,7 @@ FormAutofillParent.prototype = {
|
|||
}
|
||||
this._initialized = true;
|
||||
|
||||
Services.obs.addObserver(this, "sync-pane-loaded");
|
||||
Services.obs.addObserver(this, "privacy-pane-loaded");
|
||||
Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:GetRecords", this);
|
||||
Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
|
||||
|
@ -130,7 +130,7 @@ FormAutofillParent.prototype = {
|
|||
observe(subject, topic, data) {
|
||||
log.debug("observe:", topic, "with data:", data);
|
||||
switch (topic) {
|
||||
case "sync-pane-loaded": {
|
||||
case "privacy-pane-loaded": {
|
||||
let formAutofillPreferences = new FormAutofillPreferences();
|
||||
let document = subject.document;
|
||||
let prefFragment = formAutofillPreferences.init(document);
|
||||
|
@ -278,7 +278,7 @@ FormAutofillParent.prototype = {
|
|||
Services.ppmm.removeMessageListener("FormAutofill:GetRecords", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:SaveAddress", this);
|
||||
Services.ppmm.removeMessageListener("FormAutofill:RemoveAddresses", this);
|
||||
Services.obs.removeObserver(this, "sync-pane-loaded");
|
||||
Services.obs.removeObserver(this, "privacy-pane-loaded");
|
||||
Services.prefs.removeObserver(ENABLED_AUTOFILL_ADDRESSES_PREF, this);
|
||||
|
||||
if (FormAutofill.isAutofillCreditCardsAvailable) {
|
||||
|
|
|
@ -115,6 +115,24 @@ const Preferences = window.Preferences = (function() {
|
|||
}
|
||||
},
|
||||
|
||||
updateQueued: false,
|
||||
|
||||
updateAllElements() {
|
||||
if (this.updateQueued) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateQueued = true;
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
const preferences = Preferences.getAll();
|
||||
for (const preference of preferences) {
|
||||
preference.updateElements();
|
||||
}
|
||||
this.updateQueued = false;
|
||||
});
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
Services.prefs.removeObserver("", this);
|
||||
},
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<![CDATA[
|
||||
// Just clear out the parent's cached list of radio children
|
||||
var control = this.control;
|
||||
window.customElements.upgrade(control);
|
||||
if (control)
|
||||
control.radioChildConstructed(this);
|
||||
]]>
|
||||
|
|
Загрузка…
Ссылка в новой задаче