tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prefs-presets.sys.mjs (17245B)


      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 // @ts-check
      5 
      6 /**
      7 * @typedef {import("perf").PageContext} PageContext
      8 * @typedef {import("perf").PerformancePref} PerformancePref
      9 * @typedef {import("perf").PrefObserver} PrefObserver
     10 * @typedef {import("perf").PrefPostfix} PrefPostfix
     11 * @typedef {import("perf").Presets} Presets
     12 * @typedef {import("perf").ProfilerViewMode} ProfilerViewMode
     13 * @typedef {import("perf").RecordingSettings} RecordingSettings
     14 */
     15 
     16 /** @type {PerformancePref["Entries"]} */
     17 const ENTRIES_PREF = "devtools.performance.recording.entries";
     18 /** @type {PerformancePref["Interval"]} */
     19 const INTERVAL_PREF = "devtools.performance.recording.interval";
     20 /** @type {PerformancePref["Features"]} */
     21 const FEATURES_PREF = "devtools.performance.recording.features";
     22 /** @type {PerformancePref["Threads"]} */
     23 const THREADS_PREF = "devtools.performance.recording.threads";
     24 /** @type {PerformancePref["ObjDirs"]} */
     25 const OBJDIRS_PREF = "devtools.performance.recording.objdirs";
     26 /** @type {PerformancePref["Duration"]} */
     27 const DURATION_PREF = "devtools.performance.recording.duration";
     28 /** @type {PerformancePref["Preset"]} */
     29 const PRESET_PREF = "devtools.performance.recording.preset";
     30 /** @type {PerformancePref["PopupFeatureFlag"]} */
     31 const POPUP_FEATURE_FLAG_PREF = "devtools.performance.popup.feature-flag";
     32 /* This will be used to observe all profiler-related prefs. */
     33 const PREF_PREFIX = "devtools.performance.recording.";
     34 
     35 // The presets that we find in all interfaces are defined here.
     36 
     37 // The property l10nIds contain all FTL l10n IDs for these cases:
     38 // - properties in "popup" are used in the popup's select box.
     39 // - properties in "devtools" are used in other UIs (about:profiling and devtools panels).
     40 //
     41 // Properties for both cases have the same values, but because they're not used
     42 // in the same way we need to duplicate them.
     43 // Their values for the en-US locale are in the files:
     44 //   devtools/client/locales/en-US/perftools.ftl
     45 //   browser/locales/en-US/browser/appmenu.ftl
     46 //
     47 // IMPORTANT NOTE: Please keep the existing profiler presets in sync with their
     48 // Fenix counterparts and consider adding any new presets to Fenix:
     49 // https://searchfox.org/firefox-main/rev/d87eb30d610a3032111f9ee47441b53927de63d3/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/perf/ProfilerUtils.kt
     50 
     51 /** @type {Presets} */
     52 export const presets = {
     53  "web-developer": {
     54    entries: 128 * 1024 * 1024,
     55    interval: 1,
     56    features: ["screenshots", "js", "cpu", "memory"],
     57    threads: ["GeckoMain", "Compositor", "Renderer", "DOM Worker"],
     58    duration: 0,
     59    profilerViewMode: "active-tab",
     60    l10nIds: {
     61      popup: {
     62        label: "profiler-popup-presets-web-developer-label",
     63        description: "profiler-popup-presets-web-developer-description",
     64      },
     65      devtools: {
     66        label: "perftools-presets-web-developer-label",
     67        description: "perftools-presets-web-developer-description",
     68      },
     69    },
     70  },
     71  "firefox-platform": {
     72    entries: 128 * 1024 * 1024,
     73    interval: 1,
     74    features: [
     75      "screenshots",
     76      "js",
     77      "stackwalk",
     78      "cpu",
     79      "java",
     80      "processcpu",
     81      "memory",
     82    ],
     83    threads: [
     84      "GeckoMain",
     85      "Compositor",
     86      "Renderer",
     87      "SwComposite",
     88      "DOM Worker",
     89    ],
     90    duration: 0,
     91    l10nIds: {
     92      popup: {
     93        label: "profiler-popup-presets-firefox-label",
     94        description: "profiler-popup-presets-firefox-description",
     95      },
     96      devtools: {
     97        label: "perftools-presets-firefox-label",
     98        description: "perftools-presets-firefox-description",
     99      },
    100    },
    101  },
    102  graphics: {
    103    entries: 128 * 1024 * 1024,
    104    interval: 1,
    105    features: ["stackwalk", "js", "cpu", "java", "processcpu", "memory"],
    106    threads: [
    107      "GeckoMain",
    108      "Compositor",
    109      "Renderer",
    110      "SwComposite",
    111      "RenderBackend",
    112      "GlyphRasterizer",
    113      "SceneBuilder",
    114      "WrWorker",
    115      "CanvasWorkers",
    116      "TextureUpdate",
    117    ],
    118    duration: 0,
    119    l10nIds: {
    120      popup: {
    121        label: "profiler-popup-presets-graphics-label",
    122        description: "profiler-popup-presets-graphics-description",
    123      },
    124      devtools: {
    125        label: "perftools-presets-graphics-label",
    126        description: "perftools-presets-graphics-description",
    127      },
    128    },
    129  },
    130  media: {
    131    entries: 128 * 1024 * 1024,
    132    interval: 1,
    133    features: [
    134      "js",
    135      "stackwalk",
    136      "cpu",
    137      "audiocallbacktracing",
    138      "ipcmessages",
    139      "processcpu",
    140      "memory",
    141    ],
    142    threads: [
    143      "BackgroundThreadPool",
    144      "Compositor",
    145      "DOM Worker",
    146      "GeckoMain",
    147      "IPDL Background",
    148      "InotifyEventThread",
    149      "ModuleProcessThread",
    150      "PacerThread",
    151      "RemVidChild",
    152      "RenderBackend",
    153      "Renderer",
    154      "Socket Thread",
    155      "SwComposite",
    156      "TextureUpdate",
    157      "audio",
    158      "camera",
    159      "capture",
    160      "cubeb",
    161      "decoder",
    162      "gmp",
    163      "graph",
    164      "grph",
    165      "media",
    166      "webrtc",
    167    ],
    168    duration: 0,
    169    l10nIds: {
    170      popup: {
    171        label: "profiler-popup-presets-media-label",
    172        description: "profiler-popup-presets-media-description2",
    173      },
    174      devtools: {
    175        label: "perftools-presets-media-label",
    176        description: "perftools-presets-media-description2",
    177      },
    178    },
    179  },
    180  ml: {
    181    entries: 128 * 1024 * 1024,
    182    interval: 1,
    183    features: ["js", "stackwalk", "cpu", "ipcmessages", "processcpu", "memory"],
    184    threads: [
    185      "BackgroundThreadPool",
    186      "DOM Worker",
    187      "GeckoMain",
    188      "IPDL Background",
    189      "onnx_worker",
    190    ],
    191    duration: 0,
    192    l10nIds: {
    193      popup: {
    194        label: "profiler-popup-presets-ml-label",
    195        description: "profiler-popup-presets-ml-description",
    196      },
    197      devtools: {
    198        label: "perftools-presets-ml-label",
    199        description: "perftools-presets-ml-description2",
    200      },
    201    },
    202  },
    203  networking: {
    204    entries: 128 * 1024 * 1024,
    205    interval: 1,
    206    features: [
    207      "screenshots",
    208      "js",
    209      "stackwalk",
    210      "cpu",
    211      "java",
    212      "processcpu",
    213      "bandwidth",
    214      "memory",
    215    ],
    216    threads: [
    217      "Cache2 I/O",
    218      "Compositor",
    219      "DNS Resolver",
    220      "DOM Worker",
    221      "GeckoMain",
    222      "Renderer",
    223      "Socket Thread",
    224      "StreamTrans",
    225      "SwComposite",
    226      "TRR Background",
    227    ],
    228    duration: 0,
    229    l10nIds: {
    230      popup: {
    231        label: "profiler-popup-presets-networking-label",
    232        description: "profiler-popup-presets-networking-description",
    233      },
    234      devtools: {
    235        label: "perftools-presets-networking-label",
    236        description: "perftools-presets-networking-description",
    237      },
    238    },
    239  },
    240  power: {
    241    entries: 128 * 1024 * 1024,
    242    interval: 10,
    243    features: [
    244      "screenshots",
    245      "js",
    246      "stackwalk",
    247      "cpu",
    248      "processcpu",
    249      "nostacksampling",
    250      "ipcmessages",
    251      "markersallthreads",
    252      "power",
    253      "bandwidth",
    254      "memory",
    255    ],
    256    threads: ["GeckoMain", "Renderer"],
    257    duration: 0,
    258    l10nIds: {
    259      popup: {
    260        label: "profiler-popup-presets-power-label",
    261        description: "profiler-popup-presets-power-description",
    262      },
    263      devtools: {
    264        label: "perftools-presets-power-label",
    265        description: "perftools-presets-power-description",
    266      },
    267    },
    268  },
    269  debug: {
    270    entries: 128 * 1024 * 1024,
    271    interval: 1,
    272    features: [
    273      "cpu",
    274      "ipcmessages",
    275      "js",
    276      "markersallthreads",
    277      "processcpu",
    278      "samplingallthreads",
    279      "stackwalk",
    280      "unregisteredthreads",
    281      "flows",
    282    ],
    283    threads: ["*"],
    284    duration: 0,
    285    l10nIds: {
    286      popup: {
    287        label: "profiler-popup-presets-debug-label",
    288        description: "profiler-popup-presets-debug-description",
    289      },
    290      devtools: {
    291        label: "perftools-presets-debug-label",
    292        description: "perftools-presets-debug-description",
    293      },
    294    },
    295  },
    296  "web-compat": {
    297    entries: 128 * 1024 * 1024,
    298    interval: 1,
    299    features: ["screenshots", "js", "stackwalk", "nostacksampling", "tracing"],
    300    threads: ["GeckoMain", "DOM Worker"],
    301    mozLogs: "console: 5, PageMessages: 5",
    302    duration: 0,
    303    profilerViewMode: "active-tab",
    304    l10nIds: {
    305      popup: {
    306        label: "profiler-popup-presets-web-compat-label",
    307        description: "profiler-popup-presets-web-compat-description",
    308      },
    309      devtools: {
    310        label: "perftools-presets-web-compat-label",
    311        description: "perftools-presets-web-compat-description",
    312      },
    313    },
    314  },
    315 };
    316 
    317 /**
    318 * @param {string} prefName
    319 * @return {string[]}
    320 */
    321 function _getArrayOfStringsPref(prefName) {
    322  const text = Services.prefs.getCharPref(prefName);
    323  return JSON.parse(text);
    324 }
    325 
    326 /**
    327 * The profiler recording workflow uses two different pref paths. One set of prefs
    328 * is stored for local profiling, and another for remote profiling. This function
    329 * decides which to use. The remote prefs have ".remote" appended to the end of
    330 * their pref names.
    331 *
    332 * @param {PageContext} pageContext
    333 * @returns {PrefPostfix}
    334 */
    335 export function getPrefPostfix(pageContext) {
    336  switch (pageContext) {
    337    case "devtools":
    338    case "aboutprofiling":
    339    case "aboutlogging":
    340      // Don't use any postfix on the prefs.
    341      return "";
    342    case "devtools-remote":
    343    case "aboutprofiling-remote":
    344      return ".remote";
    345    default: {
    346      const { UnhandledCaseError } = ChromeUtils.importESModule(
    347        "resource://devtools/shared/performance-new/errors.sys.mjs",
    348        { global: "contextual" }
    349      );
    350      throw new UnhandledCaseError(pageContext, "Page Context");
    351    }
    352  }
    353 }
    354 
    355 /**
    356 * @param {string[]} objdirs
    357 */
    358 function setObjdirPrefValue(objdirs) {
    359  Services.prefs.setCharPref(OBJDIRS_PREF, JSON.stringify(objdirs));
    360 }
    361 
    362 /**
    363 * Before Firefox 92, the objdir lists for local and remote profiling were
    364 * stored in separate lists. In Firefox 92 those two prefs were merged into
    365 * one. This function performs the migration.
    366 */
    367 function migrateObjdirsPrefsIfNeeded() {
    368  const OLD_REMOTE_OBJDIRS_PREF = OBJDIRS_PREF + ".remote";
    369  const remoteString = Services.prefs.getCharPref(OLD_REMOTE_OBJDIRS_PREF, "");
    370  if (remoteString === "") {
    371    // No migration necessary.
    372    return;
    373  }
    374 
    375  const remoteList = JSON.parse(remoteString);
    376  const localList = _getArrayOfStringsPref(OBJDIRS_PREF);
    377 
    378  // Merge the two lists, eliminating any duplicates.
    379  const mergedList = [...new Set(localList.concat(remoteList))];
    380  setObjdirPrefValue(mergedList);
    381  Services.prefs.clearUserPref(OLD_REMOTE_OBJDIRS_PREF);
    382 }
    383 
    384 /**
    385 * @returns {string[]}
    386 */
    387 export function getObjdirPrefValue() {
    388  migrateObjdirsPrefsIfNeeded();
    389  return _getArrayOfStringsPref(OBJDIRS_PREF);
    390 }
    391 
    392 /**
    393 * @param {string[]} supportedFeatures
    394 * @param {string[]} objdirs
    395 * @param {PrefPostfix} prefPostfix
    396 * @return {RecordingSettings}
    397 */
    398 export function getRecordingSettingsFromPrefs(
    399  supportedFeatures,
    400  objdirs,
    401  prefPostfix
    402 ) {
    403  // If you add a new preference here, please do not forget to update
    404  // `revertRecordingSettings` as well.
    405 
    406  const entries = Services.prefs.getIntPref(ENTRIES_PREF + prefPostfix);
    407  const intervalInMicroseconds = Services.prefs.getIntPref(
    408    INTERVAL_PREF + prefPostfix
    409  );
    410  const interval = intervalInMicroseconds / 1000;
    411  const features = _getArrayOfStringsPref(FEATURES_PREF + prefPostfix);
    412  const threads = _getArrayOfStringsPref(THREADS_PREF + prefPostfix);
    413  const duration = Services.prefs.getIntPref(DURATION_PREF + prefPostfix);
    414 
    415  return {
    416    presetName: "custom",
    417    entries,
    418    interval,
    419    // Validate the features before passing them to the profiler.
    420    features: features.filter(feature => supportedFeatures.includes(feature)),
    421    threads,
    422    objdirs,
    423    duration,
    424  };
    425 }
    426 
    427 /**
    428 * @param {PageContext} pageContext
    429 * @param {RecordingSettings} prefs
    430 */
    431 export function setRecordingSettings(pageContext, prefs) {
    432  const prefPostfix = getPrefPostfix(pageContext);
    433  Services.prefs.setCharPref(PRESET_PREF + prefPostfix, prefs.presetName);
    434  Services.prefs.setIntPref(ENTRIES_PREF + prefPostfix, prefs.entries);
    435  // The interval pref stores the value in microseconds for extra precision.
    436  const intervalInMicroseconds = prefs.interval * 1000;
    437  Services.prefs.setIntPref(
    438    INTERVAL_PREF + prefPostfix,
    439    intervalInMicroseconds
    440  );
    441  Services.prefs.setCharPref(
    442    FEATURES_PREF + prefPostfix,
    443    JSON.stringify(prefs.features)
    444  );
    445  Services.prefs.setCharPref(
    446    THREADS_PREF + prefPostfix,
    447    JSON.stringify(prefs.threads)
    448  );
    449  setObjdirPrefValue(prefs.objdirs);
    450 }
    451 
    452 /**
    453 * Revert the recording prefs for both local and remote profiling.
    454 *
    455 * @return {void}
    456 */
    457 export function revertRecordingSettings() {
    458  for (const prefPostfix of ["", ".remote"]) {
    459    Services.prefs.clearUserPref(PRESET_PREF + prefPostfix);
    460    Services.prefs.clearUserPref(ENTRIES_PREF + prefPostfix);
    461    Services.prefs.clearUserPref(INTERVAL_PREF + prefPostfix);
    462    Services.prefs.clearUserPref(FEATURES_PREF + prefPostfix);
    463    Services.prefs.clearUserPref(THREADS_PREF + prefPostfix);
    464    Services.prefs.clearUserPref(DURATION_PREF + prefPostfix);
    465  }
    466  Services.prefs.clearUserPref(OBJDIRS_PREF);
    467  Services.prefs.clearUserPref(POPUP_FEATURE_FLAG_PREF);
    468 }
    469 
    470 /**
    471 * Add an observer for the profiler-related preferences.
    472 *
    473 * @param {PrefObserver} observer
    474 * @return {void}
    475 */
    476 export function addPrefObserver(observer) {
    477  Services.prefs.addObserver(PREF_PREFIX, observer);
    478 }
    479 
    480 /**
    481 * Removes an observer for the profiler-related preferences.
    482 *
    483 * @param {PrefObserver} observer
    484 * @return {void}
    485 */
    486 export function removePrefObserver(observer) {
    487  Services.prefs.removeObserver(PREF_PREFIX, observer);
    488 }
    489 /**
    490 * Return the proper view mode for the Firefox Profiler front-end timeline by
    491 * looking at the proper preset that is selected.
    492 * Return value can be undefined when the preset is unknown or custom.
    493 *
    494 * @param {PageContext} pageContext
    495 * @return {ProfilerViewMode | undefined}
    496 */
    497 export function getProfilerViewModeForCurrentPreset(pageContext) {
    498  const prefPostfix = getPrefPostfix(pageContext);
    499  const presetName = Services.prefs.getCharPref(PRESET_PREF + prefPostfix);
    500 
    501  if (presetName === "custom") {
    502    return undefined;
    503  }
    504 
    505  const preset = presets[presetName];
    506  if (!preset) {
    507    console.error(`Unknown profiler preset was encountered: "${presetName}"`);
    508    return undefined;
    509  }
    510  return preset.profilerViewMode;
    511 }
    512 
    513 /**
    514 * @param {string} presetName
    515 * @param {string[]} supportedFeatures
    516 * @param {string[]} objdirs
    517 * @return {RecordingSettings | null}
    518 */
    519 export function getRecordingSettingsFromPreset(
    520  presetName,
    521  supportedFeatures,
    522  objdirs
    523 ) {
    524  if (presetName === "custom") {
    525    return null;
    526  }
    527 
    528  const preset = presets[presetName];
    529  if (!preset) {
    530    console.error(`Unknown profiler preset was encountered: "${presetName}"`);
    531    return null;
    532  }
    533 
    534  return {
    535    presetName,
    536    entries: preset.entries,
    537    interval: preset.interval,
    538    // Validate the features before passing them to the profiler.
    539    features: preset.features.filter(feature =>
    540      supportedFeatures.includes(feature)
    541    ),
    542    threads: preset.threads,
    543    mozLogs: preset.mozLogs,
    544    objdirs,
    545    duration: preset.duration,
    546  };
    547 }
    548 
    549 /**
    550 * @param {PageContext} pageContext
    551 * @param {string[]} supportedFeatures
    552 * @returns {RecordingSettings}
    553 */
    554 export function getRecordingSettings(pageContext, supportedFeatures) {
    555  const objdirs = getObjdirPrefValue();
    556  const prefPostfix = getPrefPostfix(pageContext);
    557  const presetName = Services.prefs.getCharPref(PRESET_PREF + prefPostfix);
    558 
    559  // First try to get the values from a preset. If the preset is "custom" or
    560  // unrecognized, getRecordingSettingsFromPreset will return null and we will
    561  // get the settings from individual prefs instead.
    562  return (
    563    getRecordingSettingsFromPreset(presetName, supportedFeatures, objdirs) ??
    564    getRecordingSettingsFromPrefs(supportedFeatures, objdirs, prefPostfix)
    565  );
    566 }
    567 
    568 /**
    569 * Change the prefs based on a preset. This mechanism is used by the popup to
    570 * easily switch between different settings.
    571 *
    572 * @param {string} presetName
    573 * @param {PageContext} pageContext
    574 * @param {string[]} supportedFeatures
    575 * @return {void}
    576 */
    577 export function changePreset(pageContext, presetName, supportedFeatures) {
    578  const prefPostfix = getPrefPostfix(pageContext);
    579  const objdirs = getObjdirPrefValue();
    580  let recordingSettings = getRecordingSettingsFromPreset(
    581    presetName,
    582    supportedFeatures,
    583    objdirs
    584  );
    585 
    586  if (!recordingSettings) {
    587    // No recordingSettings were found for that preset. Most likely this means this
    588    // is a custom preset, or it's one that we dont recognize for some reason.
    589    // Get the preferences from the individual preference values.
    590    Services.prefs.setCharPref(PRESET_PREF + prefPostfix, presetName);
    591    recordingSettings = getRecordingSettingsFromPrefs(
    592      supportedFeatures,
    593      objdirs,
    594      prefPostfix
    595    );
    596  }
    597 
    598  setRecordingSettings(pageContext, recordingSettings);
    599 }