Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* This module provides a workaround for remote debugging when a preference is
* defined in the firefox preference file (browser/app/profile/firefox.js) but
* still read from the server, without any default value.
*
* This causes the server to crash and can't easily be recovered.
*
* While we work on better linting to prevent such issues (Bug 1660182), this
* module will be able to set default values for all missing preferences.
*/
const PREFERENCE_TYPES = {
BOOL: "BOOL",
CHAR: "CHAR",
INT: "INT",
};
exports.PREFERENCE_TYPES = PREFERENCE_TYPES;
/**
* Expected properties for the preference descriptors:
* - prefName {String}: the name of the preference.
* - defaultValue {String|Bool|Number}: the value to set if the preference is
* missing.
* - trait {String}: the name of the trait corresponding to this pref on the
* PreferenceFront.
* - type {String}: the preference type (either BOOL, CHAR or INT).
*/
const DEFAULT_PREFERENCES = [];
exports.DEFAULT_PREFERENCES = DEFAULT_PREFERENCES;
const METHODS = {
[PREFERENCE_TYPES.BOOL]: {
setPref: "setBoolPref",
getPref: "getBoolPref",
},
[PREFERENCE_TYPES.CHAR]: {
setPref: "setCharPref",
getPref: "getCharPref",
},
[PREFERENCE_TYPES.INT]: {
setPref: "setIntPref",
getPref: "getIntPref",
},
};
/**
* Set default values for all the provided preferences on the runtime
* corresponding to the provided clientWrapper, if needed.
*
* Note: prefDescriptors will most likely be DEFAULT_PREFERENCES when
* used in production code, but can be parameterized for tests.
*
* @param {ClientWrapper} clientWrapper
* @param {Array} prefDescriptors
* Array of preference descriptors, see DEFAULT_PREFERENCES.
*/
async function setDefaultPreferencesIfNeeded(clientWrapper, prefDescriptors) {
if (!prefDescriptors || prefDescriptors.length === 0) {
return;
}
const preferenceFront = await clientWrapper.getFront("preference");
const preferenceTraits = await preferenceFront.getTraits();
// Note: using Promise.all here fails because the request/responses get mixed.
for (const prefDescriptor of prefDescriptors) {
// If the fix for this preference is already on this server, skip it.
if (preferenceTraits[prefDescriptor.trait]) {
continue;
}
await setDefaultPreference(preferenceFront, prefDescriptor);
}
}
exports.setDefaultPreferencesIfNeeded = setDefaultPreferencesIfNeeded;
async function setDefaultPreference(preferenceFront, prefDescriptor) {
const { prefName, type, defaultValue } = prefDescriptor;
if (!Object.values(PREFERENCE_TYPES).includes(type)) {
throw new Error(`Unsupported type for setDefaultPreference "${type}"`);
}
const prefMethods = METHODS[type];
try {
// Try to read the preference only to check if the call is successful.
// If not, this means the preference is missing and should be initialized.
await preferenceFront[prefMethods.getPref](prefName);
} catch (e) {
console.warn(
`Preference "${prefName}"" is not set on the remote runtime. Setting default value.`
);
await preferenceFront[prefMethods.setPref](prefName, defaultValue);
}
}