runtime-default-preferences.js (3402B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 /** 8 * This module provides a workaround for remote debugging when a preference is 9 * defined in the firefox preference file (browser/app/profile/firefox.js) but 10 * still read from the server, without any default value. 11 * 12 * This causes the server to crash and can't easily be recovered. 13 * 14 * While we work on better linting to prevent such issues (Bug 1660182), this 15 * module will be able to set default values for all missing preferences. 16 */ 17 18 const PREFERENCE_TYPES = { 19 BOOL: "BOOL", 20 CHAR: "CHAR", 21 INT: "INT", 22 }; 23 exports.PREFERENCE_TYPES = PREFERENCE_TYPES; 24 25 /** 26 * Expected properties for the preference descriptors: 27 * - prefName {String}: the name of the preference. 28 * - defaultValue {String|Bool|Number}: the value to set if the preference is 29 * missing. 30 * - trait {String}: the name of the trait corresponding to this pref on the 31 * PreferenceFront. 32 * - type {String}: the preference type (either BOOL, CHAR or INT). 33 */ 34 const DEFAULT_PREFERENCES = []; 35 exports.DEFAULT_PREFERENCES = DEFAULT_PREFERENCES; 36 37 const METHODS = { 38 [PREFERENCE_TYPES.BOOL]: { 39 setPref: "setBoolPref", 40 getPref: "getBoolPref", 41 }, 42 [PREFERENCE_TYPES.CHAR]: { 43 setPref: "setCharPref", 44 getPref: "getCharPref", 45 }, 46 [PREFERENCE_TYPES.INT]: { 47 setPref: "setIntPref", 48 getPref: "getIntPref", 49 }, 50 }; 51 52 /** 53 * Set default values for all the provided preferences on the runtime 54 * corresponding to the provided clientWrapper, if needed. 55 * 56 * Note: prefDescriptors will most likely be DEFAULT_PREFERENCES when 57 * used in production code, but can be parameterized for tests. 58 * 59 * @param {ClientWrapper} clientWrapper 60 * @param {Array} prefDescriptors 61 * Array of preference descriptors, see DEFAULT_PREFERENCES. 62 */ 63 async function setDefaultPreferencesIfNeeded(clientWrapper, prefDescriptors) { 64 if (!prefDescriptors || prefDescriptors.length === 0) { 65 return; 66 } 67 68 const preferenceFront = await clientWrapper.getFront("preference"); 69 const preferenceTraits = await preferenceFront.getTraits(); 70 71 // Note: using Promise.all here fails because the request/responses get mixed. 72 for (const prefDescriptor of prefDescriptors) { 73 // If the fix for this preference is already on this server, skip it. 74 if (preferenceTraits[prefDescriptor.trait]) { 75 continue; 76 } 77 78 await setDefaultPreference(preferenceFront, prefDescriptor); 79 } 80 } 81 exports.setDefaultPreferencesIfNeeded = setDefaultPreferencesIfNeeded; 82 83 async function setDefaultPreference(preferenceFront, prefDescriptor) { 84 const { prefName, type, defaultValue } = prefDescriptor; 85 86 if (!Object.values(PREFERENCE_TYPES).includes(type)) { 87 throw new Error(`Unsupported type for setDefaultPreference "${type}"`); 88 } 89 90 const prefMethods = METHODS[type]; 91 try { 92 // Try to read the preference only to check if the call is successful. 93 // If not, this means the preference is missing and should be initialized. 94 await preferenceFront[prefMethods.getPref](prefName); 95 } catch (e) { 96 console.warn( 97 `Preference "${prefName}"" is not set on the remote runtime. Setting default value.` 98 ); 99 await preferenceFront[prefMethods.setPref](prefName, defaultValue); 100 } 101 }