tor-browser

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

browser_startup_syncIPC.js (13388B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /* This test sync IPC done on the main thread during startup. */
      5 
      6 "use strict";
      7 
      8 // Shortcuts for conditions.
      9 const LINUX = AppConstants.platform == "linux";
     10 const WIN = AppConstants.platform == "win";
     11 const MAC = AppConstants.platform == "macosx";
     12 const WEBRENDER = window.windowUtils.layerManagerType.startsWith("WebRender");
     13 const SKELETONUI = Services.prefs.getBoolPref(
     14  "browser.startup.preXulSkeletonUI",
     15  false
     16 );
     17 // GPUPROCESS is approximate; any check for this should have "ignoreIfUnused: true".
     18 const GPUPROCESS =
     19  ((WIN || LINUX || MAC) &&
     20    Services.prefs.getBoolPref("layers.gpu-process.enabled")) ||
     21  Services.prefs.getBoolPref("layers.gpu-process.force-enabled");
     22 
     23 /*
     24 * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
     25 * without this the test is strict and will fail if a list entry isn't used.
     26 */
     27 const startupPhases = {
     28  // Anything done before or during app-startup must have a compelling reason
     29  // to run before we have even selected the user profile.
     30  "before profile selection": [],
     31 
     32  "before opening first browser window": [],
     33 
     34  // We reach this phase right after showing the first browser window.
     35  // This means that any I/O at this point delayed first paint.
     36  "before first paint": [
     37    {
     38      name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
     39      condition: (MAC || LINUX) && !WEBRENDER,
     40      maxCount: 1,
     41    },
     42    {
     43      name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
     44      condition: WIN && !WEBRENDER,
     45      maxCount: 3,
     46    },
     47    {
     48      name: "PWebRenderBridge::Msg_EnsureConnected",
     49      condition: WIN && WEBRENDER,
     50      maxCount: 3,
     51    },
     52    {
     53      name: "PWebRenderBridge::Msg_EnsureConnected",
     54      condition: (MAC || LINUX) && WEBRENDER,
     55      maxCount: 1,
     56    },
     57    {
     58      // bug 1373773
     59      name: "PCompositorBridge::Msg_NotifyChildCreated",
     60      condition: !GPUPROCESS,
     61      ignoreIfUnused: true,
     62      maxCount: 1,
     63    },
     64    {
     65      // bug 1373773
     66      name: "PCompositorBridge::Msg_NotifyChildCreated",
     67      condition: GPUPROCESS,
     68      ignoreIfUnused: true,
     69      maxCount: 2,
     70    },
     71    {
     72      name: "PCompositorBridge::Msg_MapAndNotifyChildCreated",
     73      condition: GPUPROCESS,
     74      ignoreIfUnused: true,
     75      maxCount: 2,
     76    },
     77    {
     78      name: "PCompositorBridge::Msg_FlushRendering",
     79      condition: MAC,
     80      maxCount: 1,
     81    },
     82    {
     83      name: "PCompositorBridge::Msg_FlushRendering",
     84      condition: WIN,
     85      ignoreIfUnused: true, // Only on Win7 32
     86      maxCount: 1,
     87    },
     88    {
     89      name: "PCompositorBridge::Msg_Initialize",
     90      condition: GPUPROCESS,
     91      ignoreIfUnused: true,
     92      maxCount: 3,
     93    },
     94    {
     95      name: "PCompositorWidget::Msg_Initialize",
     96      condition: WIN,
     97      ignoreIfUnused: true, // Only on Win10 64
     98      maxCount: 3,
     99    },
    100    {
    101      name: "PGPU::Msg_AddLayerTreeIdMapping",
    102      condition: GPUPROCESS,
    103      ignoreIfUnused: true,
    104      maxCount: 5,
    105    },
    106    {
    107      name: "PCompositorBridge::Msg_MakeSnapshot",
    108      condition: WIN && !WEBRENDER,
    109      ignoreIfUnused: true, // Only on Win10 64
    110      maxCount: 1,
    111    },
    112    {
    113      name: "PWebRenderBridge::Msg_GetSnapshot",
    114      condition: WIN && WEBRENDER,
    115      ignoreIfUnused: true, // Sometimes in the next phase on Windows10 QR
    116      maxCount: 1,
    117    },
    118    {
    119      name: "PCompositorBridge::Msg_WillClose",
    120      condition: WIN,
    121      ignoreIfUnused: true, // Only on Win10 64
    122      maxCount: 2,
    123    },
    124    {
    125      name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
    126      condition: WIN,
    127      ignoreIfUnused: true, // Only on Win10 64
    128      maxCount: 2,
    129    },
    130    {
    131      name: "PGPU::Msg_GetDeviceStatus",
    132      // bug 1553740 might want to drop the WEBRENDER clause here.
    133      // Additionally, the skeleton UI causes us to attach "before first paint" to a
    134      // later event, which lets this sneak in.
    135      condition: WIN && (WEBRENDER || SKELETONUI),
    136      // If Init() completes before we call EnsureGPUReady we won't send GetDeviceStatus
    137      // so we can safely ignore if unused.
    138      ignoreIfUnused: true,
    139      maxCount: 1,
    140    },
    141    {
    142      // bug 1784869
    143      // We use Resume signal to propagate correct XWindow/wl_surface
    144      // to EGL compositor.
    145      name: "PCompositorBridge::Msg_Resume",
    146      condition: LINUX,
    147      ignoreIfUnused: true, // intermittently occurs in "before handling user events"
    148      maxCount: 1,
    149    },
    150  ],
    151 
    152  // We are at this phase once we are ready to handle user events.
    153  // Any IO at this phase or before gets in the way of the user
    154  // interacting with the first browser window.
    155  "before handling user events": [
    156    {
    157      name: "PCompositorBridge::Msg_FlushRendering",
    158      condition: MAC,
    159      ignoreIfUnused: true,
    160      maxCount: 1,
    161    },
    162    {
    163      name: "PCompositorBridge::Msg_FlushRendering",
    164      condition: LINUX,
    165      ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
    166      maxCount: 2,
    167    },
    168    {
    169      name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
    170      condition: (!MAC && !WEBRENDER) || (WIN && WEBRENDER),
    171      ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
    172      maxCount: 1,
    173    },
    174    {
    175      name: "PCompositorBridge::Msg_Initialize",
    176      condition: WIN,
    177      ignoreIfUnused: true, // Only on Win10 64
    178      maxCount: 1,
    179    },
    180    {
    181      name: "PCompositorWidget::Msg_Initialize",
    182      condition: WIN,
    183      ignoreIfUnused: true, // Only on Win10 64
    184      maxCount: 1,
    185    },
    186    {
    187      name: "PCompositorBridge::Msg_WillClose",
    188      condition: WIN,
    189      ignoreIfUnused: true, // Only on Win7 32
    190      maxCount: 2,
    191    },
    192    {
    193      name: "PCompositorBridge::Msg_MakeSnapshot",
    194      condition: WIN,
    195      ignoreIfUnused: true, // Only on Win7 32
    196      maxCount: 1,
    197    },
    198    {
    199      name: "PWebRenderBridge::Msg_GetSnapshot",
    200      condition: WIN && WEBRENDER,
    201      ignoreIfUnused: true, // Sometimes in the next phase on Windows10 QR
    202      maxCount: 1,
    203    },
    204    {
    205      name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
    206      condition: WIN,
    207      ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
    208      maxCount: 1,
    209    },
    210    {
    211      name: "PAPZInputBridge::Msg_ReceiveMouseInputEvent",
    212      condition: WIN,
    213      ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
    214      maxCount: 1,
    215    },
    216    {
    217      name: "PWebRenderBridge::Msg_EnsureConnected",
    218      condition: WIN && WEBRENDER,
    219      ignoreIfUnused: true,
    220      maxCount: 1,
    221    },
    222    {
    223      name: "PContent::Reply_BeginDriverCrashGuard",
    224      condition: WIN,
    225      ignoreIfUnused: true, // Bug 1660590 - found while running test on windows hardware
    226      maxCount: 1,
    227    },
    228    {
    229      name: "PContent::Reply_EndDriverCrashGuard",
    230      condition: WIN,
    231      ignoreIfUnused: true, // Bug 1660590 - found while running test on windows hardware
    232      maxCount: 1,
    233    },
    234    {
    235      // bug 1784869
    236      // We use Resume signal to propagate correct XWindow/wl_surface
    237      // to EGL compositor.
    238      name: "PCompositorBridge::Msg_Resume",
    239      condition: LINUX,
    240      ignoreIfUnused: true, // intermittently occurs in "before first paint"
    241      maxCount: 1,
    242    },
    243  ],
    244 
    245  // Things that are expected to be completely out of the startup path
    246  // and loaded lazily when used for the first time by the user should
    247  // be listed here.
    248  "before becoming idle": [
    249    {
    250      // bug 1373773
    251      name: "PCompositorBridge::Msg_NotifyChildCreated",
    252      ignoreIfUnused: true,
    253      maxCount: 1,
    254    },
    255    {
    256      name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
    257      condition: WIN,
    258      ignoreIfUnused: true, // Only on Win10 64
    259      maxCount: 1,
    260    },
    261    {
    262      name: "PAPZInputBridge::Msg_ReceiveMouseInputEvent",
    263      condition: WIN,
    264      ignoreIfUnused: true, // Only on Win10 64
    265      maxCount: 1,
    266    },
    267    {
    268      // bug 1554234
    269      name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
    270      condition: WIN || LINUX,
    271      ignoreIfUnused: true, // intermittently occurs in "before handling user events"
    272      maxCount: 1,
    273    },
    274    {
    275      name: "PWebRenderBridge::Msg_EnsureConnected",
    276      condition: (WIN || LINUX) && WEBRENDER,
    277      ignoreIfUnused: true,
    278      maxCount: 1,
    279    },
    280    {
    281      name: "PCompositorBridge::Msg_Initialize",
    282      condition: WIN,
    283      ignoreIfUnused: true, // Intermittently occurs in "before handling user events"
    284      maxCount: 1,
    285    },
    286    {
    287      name: "PCompositorWidget::Msg_Initialize",
    288      condition: WIN,
    289      ignoreIfUnused: true, // Intermittently occurs in "before handling user events"
    290      maxCount: 1,
    291    },
    292    {
    293      name: "PCompositorBridge::Msg_MapAndNotifyChildCreated",
    294      condition: WIN,
    295      ignoreIfUnused: true,
    296      maxCount: 1,
    297    },
    298    {
    299      name: "PCompositorBridge::Msg_FlushRendering",
    300      condition: MAC || SKELETONUI,
    301      ignoreIfUnused: true,
    302      maxCount: 1,
    303    },
    304    {
    305      name: "PCompositorBridge::Msg_FlushRendering",
    306      condition: LINUX,
    307      ignoreIfUnused: true, // intermittently occurs in "before handling user events"
    308      maxCount: 1,
    309    },
    310    {
    311      name: "PWebRenderBridge::Msg_GetSnapshot",
    312      condition: WIN && WEBRENDER,
    313      ignoreIfUnused: true,
    314      maxCount: 1,
    315    },
    316    {
    317      name: "PCompositorBridge::Msg_MakeSnapshot",
    318      condition: WIN,
    319      ignoreIfUnused: true,
    320      maxCount: 1,
    321    },
    322    {
    323      name: "PCompositorBridge::Msg_WillClose",
    324      condition: WIN,
    325      ignoreIfUnused: true,
    326      maxCount: 2,
    327    },
    328    // Added for the search-detection built-in add-on.
    329    {
    330      name: "PGPU::Msg_AddLayerTreeIdMapping",
    331      condition: GPUPROCESS,
    332      ignoreIfUnused: true,
    333      maxCount: 1,
    334    },
    335  ],
    336 };
    337 
    338 add_task(async function () {
    339  if (
    340    !AppConstants.NIGHTLY_BUILD &&
    341    !AppConstants.MOZ_DEV_EDITION &&
    342    !AppConstants.DEBUG
    343  ) {
    344    ok(
    345      !("@mozilla.org/test/startuprecorder;1" in Cc),
    346      "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
    347        "non-debug build."
    348    );
    349    return;
    350  }
    351 
    352  let startupRecorder =
    353    Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
    354  await startupRecorder.done;
    355 
    356  // Check for sync IPC markers in the startup profile.
    357  let profile = startupRecorder.data.profile.threads[0];
    358 
    359  let phases = {};
    360  {
    361    const nameCol = profile.markers.schema.name;
    362    const dataCol = profile.markers.schema.data;
    363    const startTimeCol = profile.markers.schema.startTime;
    364 
    365    let markersForCurrentPhase = [];
    366    for (let m of profile.markers.data) {
    367      let markerName = profile.stringTable[m[nameCol]];
    368      if (markerName.startsWith("startupRecorder:")) {
    369        phases[markerName.split("startupRecorder:")[1]] =
    370          markersForCurrentPhase;
    371        markersForCurrentPhase = [];
    372        continue;
    373      }
    374 
    375      let markerData = m[dataCol];
    376      if (
    377        !markerData ||
    378        markerData.category != "Sync IPC" ||
    379        !m[startTimeCol]
    380      ) {
    381        continue;
    382      }
    383 
    384      markersForCurrentPhase.push(markerName);
    385    }
    386  }
    387 
    388  for (let phase in startupPhases) {
    389    startupPhases[phase] = startupPhases[phase].filter(
    390      entry => !("condition" in entry) || entry.condition
    391    );
    392  }
    393 
    394  let shouldPass = true;
    395  for (let phase in phases) {
    396    let knownIPCList = startupPhases[phase];
    397    if (knownIPCList.length) {
    398      info(
    399        `known sync IPC ${phase}:\n` +
    400          knownIPCList
    401            .map(e => `  ${e.name} - at most ${e.maxCount} times`)
    402            .join("\n")
    403      );
    404    }
    405 
    406    let markers = phases[phase];
    407    for (let marker of markers) {
    408      let expected = false;
    409      for (let entry of knownIPCList) {
    410        if (marker == entry.name) {
    411          entry.useCount = (entry.useCount || 0) + 1;
    412          expected = true;
    413          break;
    414        }
    415      }
    416      if (!expected) {
    417        ok(false, `unexpected ${marker} sync IPC ${phase}`);
    418        shouldPass = false;
    419      }
    420    }
    421 
    422    for (let entry of knownIPCList) {
    423      // Make sure useCount has been defined.
    424      entry.useCount = entry.useCount || 0;
    425      let message = `sync IPC ${entry.name} `;
    426      if (entry.useCount == entry.maxCount) {
    427        message += "happened as many times as expected";
    428      } else if (entry.useCount < entry.maxCount) {
    429        message += `allowed ${entry.maxCount} but only happened ${entry.useCount} times`;
    430      } else {
    431        message += `happened ${entry.useCount} but max is ${entry.maxCount}`;
    432        shouldPass = false;
    433      }
    434      Assert.lessOrEqual(entry.useCount, entry.maxCount, `${message} ${phase}`);
    435 
    436      if (entry.useCount == 0 && !entry.ignoreIfUnused) {
    437        ok(false, `unused known IPC entry ${phase}: ${entry.name}`);
    438        shouldPass = false;
    439      }
    440    }
    441  }
    442 
    443  if (shouldPass) {
    444    ok(shouldPass, "No unexpected sync IPC during startup");
    445  } else {
    446    const filename = "profile_startup_syncIPC.json";
    447    let path = Services.env.get("MOZ_UPLOAD_DIR");
    448    let profilePath = PathUtils.join(path, filename);
    449    await IOUtils.writeJSON(profilePath, startupRecorder.data.profile);
    450    ok(
    451      false,
    452      `Unexpected sync IPC behavior during startup; open the ${filename} ` +
    453        "artifact in the Firefox Profiler to see what happened"
    454    );
    455  }
    456 });