browser_preferences_usage.js (7676B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 if (SpecialPowers.useRemoteSubframes) { 5 requestLongerTimeout(2); 6 } 7 8 const DEFAULT_PROCESS_COUNT = Services.prefs 9 .getDefaultBranch(null) 10 .getIntPref("dom.ipc.processCount"); 11 12 /** 13 * A test that checks whether any preference getter from the given list 14 * of stats was called more often than the max parameter. 15 * 16 * @param {Array} stats - an array of [prefName, accessCount] tuples 17 * @param {number} max - the maximum number of times any of the prefs should 18 * have been called. 19 * @param {object} knownProblematicPrefs (optional) - an object that defines 20 * prefs that should be exempt from checking the 21 * maximum access. It looks like the following: 22 * 23 * pref_name: { 24 * min: [Number] the minimum amount of times this should have 25 * been called (to avoid keeping around dead items) 26 * max: [Number] the maximum amount of times this should have 27 * been called (to avoid this creeping up further) 28 * } 29 */ 30 function checkPrefGetters(stats, max, knownProblematicPrefs = {}) { 31 let getterStats = Object.entries(stats).sort( 32 ([, val1], [, val2]) => val2 - val1 33 ); 34 35 // Clone the list to be able to delete entries to check if we 36 // forgot any later on. 37 knownProblematicPrefs = Object.assign({}, knownProblematicPrefs); 38 39 for (let [pref, count] of getterStats) { 40 let prefLimits = knownProblematicPrefs[pref]; 41 if (!prefLimits) { 42 Assert.lessOrEqual( 43 count, 44 max, 45 `${pref} should not be accessed more than ${max} times.` 46 ); 47 } else { 48 // Still record how much this pref was accessed even if we don't do any real assertions. 49 if (!prefLimits.min && !prefLimits.max) { 50 info( 51 `${pref} should not be accessed more than ${max} times and was accessed ${count} times.` 52 ); 53 } 54 55 if (prefLimits.min) { 56 Assert.lessOrEqual( 57 prefLimits.min, 58 count, 59 `${pref} should be accessed at least ${prefLimits.min} times.` 60 ); 61 } 62 if (prefLimits.max) { 63 Assert.lessOrEqual( 64 count, 65 prefLimits.max, 66 `${pref} should be accessed at most ${prefLimits.max} times.` 67 ); 68 } 69 delete knownProblematicPrefs[pref]; 70 } 71 } 72 73 let unusedPrefs = Object.keys(knownProblematicPrefs); 74 is( 75 unusedPrefs.length, 76 0, 77 `Should have accessed all known problematic prefs. Remaining: ${unusedPrefs}` 78 ); 79 } 80 81 /** 82 * A helper function to read preference access data 83 * using the Services.prefs.readStats() function. 84 */ 85 function getPreferenceStats() { 86 let stats = {}; 87 Services.prefs.readStats((key, value) => (stats[key] = value)); 88 return stats; 89 } 90 91 add_task(async function debug_only() { 92 ok(AppConstants.DEBUG, "You need to run this test on a debug build."); 93 }); 94 95 // Just checks how many prefs were accessed during startup. 96 add_task(async function startup() { 97 let max = 40; 98 99 let knownProblematicPrefs = { 100 "network.loadinfo.skip_type_assertion": { 101 // This is accessed in debug only. 102 }, 103 }; 104 105 let startupRecorder = 106 Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject; 107 await startupRecorder.done; 108 109 ok(startupRecorder.data.prefStats, "startupRecorder has prefStats"); 110 111 checkPrefGetters(startupRecorder.data.prefStats, max, knownProblematicPrefs); 112 }); 113 114 // This opens 10 tabs and checks pref getters. 115 add_task(async function open_10_tabs() { 116 // This is somewhat arbitrary. When we had a default of 4 content processes 117 // the value was 15. We need to scale it as we increase the number of 118 // content processes so we approximate with 4 * process_count. 119 const max = 4 * DEFAULT_PROCESS_COUNT; 120 121 let knownProblematicPrefs = { 122 "browser.tabs.remote.logSwitchTiming": { 123 max: 35, 124 }, 125 "network.loadinfo.skip_type_assertion": { 126 // This is accessed in debug only. 127 }, 128 }; 129 130 Services.prefs.resetStats(); 131 132 let tabs = []; 133 while (tabs.length < 10) { 134 tabs.push( 135 await BrowserTestUtils.openNewForegroundTab( 136 gBrowser, 137 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 138 "http://example.com", 139 true, 140 true 141 ) 142 ); 143 } 144 145 for (let tab of tabs) { 146 await BrowserTestUtils.removeTab(tab); 147 } 148 149 checkPrefGetters(getPreferenceStats(), max, knownProblematicPrefs); 150 }); 151 152 // This navigates to 50 sites and checks pref getters. 153 add_task(async function navigate_around() { 154 await SpecialPowers.pushPrefEnv({ 155 set: [ 156 // Disable bfcache so that we can measure more accurately the number of 157 // pref accesses in the child processes. 158 // If bfcache is enabled on Fission 159 // security.sandbox.content.force-namespace is accessed only a couple of 160 // times. 161 ["browser.sessionhistory.max_total_viewers", 0], 162 ], 163 }); 164 165 let max = 40; 166 167 let knownProblematicPrefs = { 168 "network.loadinfo.skip_type_assertion": { 169 // This is accessed in debug only. 170 }, 171 }; 172 173 if (SpecialPowers.useRemoteSubframes) { 174 // We access this when considering starting a new content process. 175 // Because there is no complete list of content process types, 176 // caching this is not trivial. Opening 50 different content 177 // processes and throwing them away immediately is a bit artificial; 178 // we're more likely to keep some around so this shouldn't be quite 179 // this bad in practice. Fixing this is 180 // https://bugzilla.mozilla.org/show_bug.cgi?id=1600266 181 knownProblematicPrefs["dom.ipc.processCount.webIsolated"] = { 182 min: 50, 183 max: 51, 184 }; 185 if (AppConstants.platform == "linux") { 186 // The following sandbox pref is covered by 187 // https://bugzilla.mozilla.org/show_bug.cgi?id=1600189 188 knownProblematicPrefs["security.sandbox.content.force-namespace"] = { 189 min: 45, 190 max: 55, 191 }; 192 } else if (AppConstants.platform == "win") { 193 // The following 2 graphics prefs are covered by 194 // https://bugzilla.mozilla.org/show_bug.cgi?id=1639497 195 knownProblematicPrefs["gfx.canvas.azure.backends"] = { 196 min: 90, 197 max: 110, 198 }; 199 knownProblematicPrefs["gfx.content.azure.backends"] = { 200 min: 90, 201 max: 110, 202 }; 203 // The following 2 sandbox prefs are covered by 204 // https://bugzilla.mozilla.org/show_bug.cgi?id=1639494 205 knownProblematicPrefs["security.sandbox.content.read_path_whitelist"] = { 206 min: 47, 207 max: 55, 208 }; 209 knownProblematicPrefs["security.sandbox.logging.enabled"] = { 210 min: 47, 211 max: 55, 212 }; 213 } 214 } 215 216 Services.prefs.resetStats(); 217 218 let tab = await BrowserTestUtils.openNewForegroundTab( 219 gBrowser, 220 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 221 "http://example.com", 222 true, 223 true 224 ); 225 226 let urls = [ 227 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 228 "http://example.com/", 229 "https://example.com/", 230 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 231 "http://example.org/", 232 "https://example.org/", 233 ]; 234 235 for (let i = 0; i < 50; i++) { 236 let url = urls[i % urls.length]; 237 info(`Navigating to ${url}...`); 238 BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, url); 239 await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url); 240 info(`Loaded ${url}.`); 241 } 242 243 await BrowserTestUtils.removeTab(tab); 244 245 checkPrefGetters(getPreferenceStats(), max, knownProblematicPrefs); 246 });