forked from mirrors/gecko-dev
450 lines
13 KiB
JavaScript
450 lines
13 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
/* This test sync IPC done on the main thread during startup. */
|
|
|
|
"use strict";
|
|
|
|
// Shortcuts for conditions.
|
|
const LINUX = AppConstants.platform == "linux";
|
|
const WIN = AppConstants.platform == "win";
|
|
const MAC = AppConstants.platform == "macosx";
|
|
const WEBRENDER = window.windowUtils.layerManagerType.startsWith("WebRender");
|
|
const SKELETONUI = Services.prefs.getBoolPref(
|
|
"browser.startup.preXulSkeletonUI",
|
|
false
|
|
);
|
|
|
|
/*
|
|
* Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
|
|
* without this the test is strict and will fail if a list entry isn't used.
|
|
*/
|
|
const startupPhases = {
|
|
// Anything done before or during app-startup must have a compelling reason
|
|
// to run before we have even selected the user profile.
|
|
"before profile selection": [],
|
|
|
|
"before opening first browser window": [],
|
|
|
|
// We reach this phase right after showing the first browser window.
|
|
// This means that any I/O at this point delayed first paint.
|
|
"before first paint": [
|
|
{
|
|
name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
|
|
condition: (MAC || LINUX) && !WEBRENDER,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
|
|
condition: WIN && !WEBRENDER,
|
|
maxCount: 3,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_EnsureConnected",
|
|
condition: WIN && WEBRENDER,
|
|
maxCount: 3,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_EnsureConnected",
|
|
condition: (MAC || LINUX) && WEBRENDER,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
// bug 1373773
|
|
name: "PCompositorBridge::Msg_NotifyChildCreated",
|
|
condition: !WIN,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_NotifyChildCreated",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win7 32
|
|
maxCount: 2,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_MapAndNotifyChildCreated",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 2,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: MAC,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win7 32
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 3,
|
|
},
|
|
{
|
|
name: "PCompositorWidget::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 3,
|
|
},
|
|
{
|
|
name: "PGPU::Msg_AddLayerTreeIdMapping",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 5,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_MakeSnapshot",
|
|
condition: WIN && !WEBRENDER,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_GetSnapshot",
|
|
condition: WIN && WEBRENDER,
|
|
ignoreIfUnused: true, // Sometimes in the next phase on Windows10 QR
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_WillClose",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 2,
|
|
},
|
|
{
|
|
name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PGPU::Msg_GetDeviceStatus",
|
|
// bug 1553740 might want to drop the WEBRENDER clause here.
|
|
// Additionally, the skeleton UI causes us to attach "before first paint" to a
|
|
// later event, which lets this sneak in.
|
|
condition: WIN && (WEBRENDER || SKELETONUI),
|
|
// If Init() completes before we call EnsureGPUReady we won't send GetDeviceStatus
|
|
// so we can safely ignore if unused.
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
// bug 1784869
|
|
// We use Resume signal to propagate correct XWindow/wl_surface
|
|
// to EGL compositor.
|
|
name: "PCompositorBridge::Msg_Resume",
|
|
condition: LINUX,
|
|
ignoreIfUnused: true, // intermittently occurs in "before handling user events"
|
|
maxCount: 1,
|
|
},
|
|
],
|
|
|
|
// We are at this phase once we are ready to handle user events.
|
|
// Any IO at this phase or before gets in the way of the user
|
|
// interacting with the first browser window.
|
|
"before handling user events": [
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: MAC,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: LINUX,
|
|
ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
|
|
maxCount: 2,
|
|
},
|
|
{
|
|
name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
|
|
condition: (!MAC && !WEBRENDER) || (WIN && WEBRENDER),
|
|
ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorWidget::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_WillClose",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win7 32
|
|
maxCount: 2,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_MakeSnapshot",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win7 32
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_GetSnapshot",
|
|
condition: WIN && WEBRENDER,
|
|
ignoreIfUnused: true, // Sometimes in the next phase on Windows10 QR
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PAPZInputBridge::Msg_ReceiveMouseInputEvent",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // intermittently occurs in "before becoming idle"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_EnsureConnected",
|
|
condition: WIN && WEBRENDER,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PContent::Reply_BeginDriverCrashGuard",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Bug 1660590 - found while running test on windows hardware
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PContent::Reply_EndDriverCrashGuard",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Bug 1660590 - found while running test on windows hardware
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
// bug 1784869
|
|
// We use Resume signal to propagate correct XWindow/wl_surface
|
|
// to EGL compositor.
|
|
name: "PCompositorBridge::Msg_Resume",
|
|
condition: LINUX,
|
|
ignoreIfUnused: true, // intermittently occurs in "before first paint"
|
|
maxCount: 1,
|
|
},
|
|
],
|
|
|
|
// Things that are expected to be completely out of the startup path
|
|
// and loaded lazily when used for the first time by the user should
|
|
// be listed here.
|
|
"before becoming idle": [
|
|
{
|
|
// bug 1373773
|
|
name: "PCompositorBridge::Msg_NotifyChildCreated",
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PAPZInputBridge::Msg_ReceiveMouseInputEvent",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Only on Win10 64
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
// bug 1554234
|
|
name: "PLayerTransaction::Msg_GetTextureFactoryIdentifier",
|
|
condition: WIN || LINUX,
|
|
ignoreIfUnused: true, // intermittently occurs in "before handling user events"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_EnsureConnected",
|
|
condition: (WIN || LINUX) && WEBRENDER,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Intermittently occurs in "before handling user events"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorWidget::Msg_Initialize",
|
|
condition: WIN,
|
|
ignoreIfUnused: true, // Intermittently occurs in "before handling user events"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_MapAndNotifyChildCreated",
|
|
condition: WIN,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: MAC || SKELETONUI,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_FlushRendering",
|
|
condition: LINUX,
|
|
ignoreIfUnused: true, // intermittently occurs in "before handling user events"
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PWebRenderBridge::Msg_GetSnapshot",
|
|
condition: WIN && WEBRENDER,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_MakeSnapshot",
|
|
condition: WIN,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
{
|
|
name: "PCompositorBridge::Msg_WillClose",
|
|
condition: WIN,
|
|
ignoreIfUnused: true,
|
|
maxCount: 2,
|
|
},
|
|
// Added for the search-detection built-in add-on.
|
|
{
|
|
name: "PGPU::Msg_AddLayerTreeIdMapping",
|
|
condition: WIN,
|
|
ignoreIfUnused: true,
|
|
maxCount: 1,
|
|
},
|
|
],
|
|
};
|
|
|
|
add_task(async function() {
|
|
if (
|
|
!AppConstants.NIGHTLY_BUILD &&
|
|
!AppConstants.MOZ_DEV_EDITION &&
|
|
!AppConstants.DEBUG
|
|
) {
|
|
ok(
|
|
!("@mozilla.org/test/startuprecorder;1" in Cc),
|
|
"the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
|
|
"non-debug build."
|
|
);
|
|
return;
|
|
}
|
|
|
|
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService()
|
|
.wrappedJSObject;
|
|
await startupRecorder.done;
|
|
|
|
// Check for sync IPC markers in the startup profile.
|
|
let profile = startupRecorder.data.profile.threads[0];
|
|
|
|
let phases = {};
|
|
{
|
|
const nameCol = profile.markers.schema.name;
|
|
const dataCol = profile.markers.schema.data;
|
|
const startTimeCol = profile.markers.schema.startTime;
|
|
|
|
let markersForCurrentPhase = [];
|
|
for (let m of profile.markers.data) {
|
|
let markerName = profile.stringTable[m[nameCol]];
|
|
if (markerName.startsWith("startupRecorder:")) {
|
|
phases[
|
|
markerName.split("startupRecorder:")[1]
|
|
] = markersForCurrentPhase;
|
|
markersForCurrentPhase = [];
|
|
continue;
|
|
}
|
|
|
|
let markerData = m[dataCol];
|
|
if (
|
|
!markerData ||
|
|
markerData.category != "Sync IPC" ||
|
|
!m[startTimeCol]
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
markersForCurrentPhase.push(markerName);
|
|
}
|
|
}
|
|
|
|
for (let phase in startupPhases) {
|
|
startupPhases[phase] = startupPhases[phase].filter(
|
|
entry => !("condition" in entry) || entry.condition
|
|
);
|
|
}
|
|
|
|
let shouldPass = true;
|
|
for (let phase in phases) {
|
|
let knownIPCList = startupPhases[phase];
|
|
if (knownIPCList.length) {
|
|
info(
|
|
`known sync IPC ${phase}:\n` +
|
|
knownIPCList
|
|
.map(e => ` ${e.name} - at most ${e.maxCount} times`)
|
|
.join("\n")
|
|
);
|
|
}
|
|
|
|
let markers = phases[phase];
|
|
for (let marker of markers) {
|
|
let expected = false;
|
|
for (let entry of knownIPCList) {
|
|
if (marker == entry.name) {
|
|
entry.useCount = (entry.useCount || 0) + 1;
|
|
expected = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!expected) {
|
|
ok(false, `unexpected ${marker} sync IPC ${phase}`);
|
|
shouldPass = false;
|
|
}
|
|
}
|
|
|
|
for (let entry of knownIPCList) {
|
|
// Make sure useCount has been defined.
|
|
entry.useCount = entry.useCount || 0;
|
|
let message = `sync IPC ${entry.name} `;
|
|
if (entry.useCount == entry.maxCount) {
|
|
message += "happened as many times as expected";
|
|
} else if (entry.useCount < entry.maxCount) {
|
|
message += `allowed ${entry.maxCount} but only happened ${entry.useCount} times`;
|
|
} else {
|
|
message += `happened ${entry.useCount} but max is ${entry.maxCount}`;
|
|
shouldPass = false;
|
|
}
|
|
ok(entry.useCount <= entry.maxCount, `${message} ${phase}`);
|
|
|
|
if (entry.useCount == 0 && !entry.ignoreIfUnused) {
|
|
ok(false, `unused known IPC entry ${phase}: ${entry.name}`);
|
|
shouldPass = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shouldPass) {
|
|
ok(shouldPass, "No unexpected sync IPC during startup");
|
|
} else {
|
|
const filename = "profile_startup_syncIPC.json";
|
|
let path = Services.env.get("MOZ_UPLOAD_DIR");
|
|
let profilePath = PathUtils.join(path, filename);
|
|
await IOUtils.writeJSON(profilePath, startupRecorder.data.profile);
|
|
ok(
|
|
false,
|
|
`Unexpected sync IPC behavior during startup; open the ${filename} ` +
|
|
"artifact in the Firefox Profiler to see what happened"
|
|
);
|
|
}
|
|
});
|