Backed out changeset ece44b8f51e4 (bug 1581418) for causing xpcshell failures.

--HG--
rename : devtools/client/webreplay/mochitest/browser_rr_object_preview-01.js => devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-03.js
This commit is contained in:
Mihai Alexandru Michis 2019-09-17 06:17:05 +03:00
Родитель 0549668bb1
Коммит 4174e65ff5
15 изменённых файлов: 336 добавлений и 565 удалений

Просмотреть файл

@ -33,11 +33,9 @@ support-files =
[browser_dbg_rr_replay-03.js] [browser_dbg_rr_replay-03.js]
[browser_dbg_rr_console_warp-01.js] [browser_dbg_rr_console_warp-01.js]
[browser_dbg_rr_console_warp-02.js] [browser_dbg_rr_console_warp-02.js]
[browser_dbg_rr_console_warp-03.js]
[browser_dbg_rr_logpoint-01.js] [browser_dbg_rr_logpoint-01.js]
[browser_dbg_rr_logpoint-02.js] [browser_dbg_rr_logpoint-02.js]
[browser_dbg_rr_logpoint-03.js]
[browser_rr_inspector-01.js] [browser_rr_inspector-01.js]
[browser_rr_inspector-02.js] [browser_rr_inspector-02.js]
[browser_rr_inspector-03.js] [browser_rr_inspector-03.js]
[browser_rr_object_preview-01.js]
[browser_rr_object_preview-02.js]

Просмотреть файл

@ -6,13 +6,51 @@
"use strict"; "use strict";
const BrowserTest = {
gTestPath,
ok,
is,
registerCleanupFunction,
waitForExplicitFinish,
BrowserTestUtils,
};
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/head.js",
BrowserTest
);
async function checkMessageObjectContents(msg, expected, expandList = []) {
const oi = msg.querySelector(".tree");
const node = oi.querySelector(".tree-node");
BrowserTest.expandObjectInspectorNode(node);
for (const label of expandList) {
const labelNode = await waitFor(() =>
BrowserTest.findObjectInspectorNode(oi, label)
);
BrowserTest.expandObjectInspectorNode(labelNode);
}
const properties = await waitFor(() => {
const nodes = BrowserTest.getObjectInspectorNodes(oi);
if (nodes && nodes.length > 1) {
return [...nodes].map(n => n.textContent);
}
return null;
});
expected.forEach(s => {
ok(properties.find(v => v.includes(s)), `Object contents include "${s}"`);
});
}
function checkJumpIcon(msg) { function checkJumpIcon(msg) {
const jumpIcon = msg.querySelector(".jump-definition"); const jumpIcon = msg.querySelector(".jump-definition");
ok(jumpIcon, "Found a jump icon"); ok(jumpIcon, "Found a jump icon");
} }
// Test the objects produced by console.log() calls and by evaluating various // Test evaluating various expressions in the console after time warping.
// expressions in the console after time warping.
add_task(async function() { add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_objects.html", { const dbg = await attachRecordingDebugger("doc_rr_objects.html", {
waitForRecording: true, waitForRecording: true,
@ -25,24 +63,18 @@ add_task(async function() {
await waitForMessage(hud, "Array(20) [ 0, 1, 2, 3, 4, 5,"); await waitForMessage(hud, "Array(20) [ 0, 1, 2, 3, 4, 5,");
await waitForMessage(hud, "Uint8Array(20) [ 0, 1, 2, 3, 4, 5,"); await waitForMessage(hud, "Uint8Array(20) [ 0, 1, 2, 3, 4, 5,");
await waitForMessage(hud, "Set(22) [ {…}, {…}, 0, 1, 2, 3, 4, 5,"); await waitForMessage(hud, "Set(22) [ null, null, 0, 1, 2, 3, 4, 5,");
await waitForMessage( await waitForMessage(
hud, hud,
"Map(21) { {…} → {…}, 0 → 1, 1 → 2, 2 → 3, 3 → 4, 4 → 5," "Map(21) { {…} → {…}, 0 → 1, 1 → 2, 2 → 3, 3 → 4, 4 → 5,"
); );
await waitForMessage(hud, "WeakSet(20) [ {…}, {…}, {…},"); await waitForMessage(hud, "WeakSet(10)");
await waitForMessage(hud, "WeakMap(20) { {…} → {…}, {…} → {…},"); await waitForMessage(hud, "WeakMap(10)");
await waitForMessage( await waitForMessage(
hud, hud,
"Object { a: 0, a0: 0, a1: 1, a2: 2, a3: 3, a4: 4," "Object { a: 0, a0: 0, a1: 1, a2: 2, a3: 3, a4: 4,"
); );
await waitForMessage(hud, "/abc/gi"); await waitForMessage(hud, "/abc/gi");
await waitForMessage(hud, "Date");
// Note: this message has an associated stack but we don't have an easy way to
// check its contents as BrowserTest.checkMessageStack requires the stack to
// be collapsed.
await waitForMessage(hud, 'RangeError: "foo"');
msg = await waitForMessage(hud, "function bar()"); msg = await waitForMessage(hud, "function bar()");
checkJumpIcon(msg); checkJumpIcon(msg);

Просмотреть файл

@ -1,38 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test event logpoints when replaying.
add_task(async function() {
await pushPref("devtools.debugger.features.log-event-breakpoints", true);
const dbg = await attachRecordingDebugger("doc_events.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await dbg.actions.addEventListenerBreakpoints(["event.mouse.mousedown"]);
const msg = await waitForMessage(hud, "mousedown");
// The message's inline preview should contain useful properties.
const regexps = [
/target: HTMLDivElement/,
/clientX: \d+/,
/clientY: \d+/,
/layerX: \d+/,
/layerY: \d+/,
];
for (const regexp of regexps) {
ok(regexp.test(msg.textContent), `Message text includes ${regexp}`);
}
// When expanded, other properties should be visible.
await checkMessageObjectContents(msg, ["altKey: false", "bubbles: true"]);
await shutdownDebugger(dbg);
});

Просмотреть файл

@ -1,44 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Inspecting objects can lead to uncaught rejections when shutting down.
PromiseTestUtils.whitelistRejectionsGlobally(
/can't be sent as the connection just closed/
);
function findNode(dbg, text) {
for (let index = 0; ; index++) {
const elem = findElement(dbg, "scopeNode", index);
if (elem && elem.innerText == text) {
return elem;
}
}
}
function toggleNode(dbg, text) {
return toggleObjectInspectorNode(findNode(dbg, text));
}
// Test that objects show up correctly in the scope pane.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_objects.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, dbg, "Done");
// We should be able to expand the window and see its properties.
await toggleNode(dbg, "<this>");
findNode(dbg, "bar()");
findNode(dbg, "baz()");
await shutdownDebugger(dbg);
});

Просмотреть файл

@ -1,18 +0,0 @@
<script src="/tests/SimpleTest/EventUtils.js"></script>
<div id="divvy">Hello World!</div>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
const divvy = document.getElementById("divvy");
divvy.addEventListener("mousedown", e => {
divvy.innerText = "Goodbye World!";
window.setTimeout(recordingFinished);
});
window.setTimeout(() => {
synthesizeMouseAtCenter(divvy, {});
});
</script>

Просмотреть файл

@ -29,7 +29,7 @@ for (let i = 0; i < 20; i++) {
} }
var h = /abc/gi; var h = /abc/gi;
var i = new Date(); var i = new Date();
var j = RangeError("foo"); var j = RangeError();
var k = document.getElementById("foo"); var k = document.getElementById("foo");
var l = bar; var l = bar;
console.log(a); console.log(a);

Просмотреть файл

@ -189,46 +189,6 @@ async function warpToMessage(hud, dbg, text) {
} }
} }
// For tests that need webconsole test features.
const BrowserTest = {
gTestPath,
ok,
is,
registerCleanupFunction,
waitForExplicitFinish,
BrowserTestUtils,
};
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/head.js",
BrowserTest
);
async function checkMessageObjectContents(msg, expected, expandList = []) {
const oi = msg.querySelector(".tree");
const node = oi.querySelector(".tree-node");
BrowserTest.expandObjectInspectorNode(node);
for (const label of expandList) {
const labelNode = await waitFor(() =>
BrowserTest.findObjectInspectorNode(oi, label)
);
BrowserTest.expandObjectInspectorNode(labelNode);
}
const properties = await waitFor(() => {
const nodes = BrowserTest.getObjectInspectorNodes(oi);
if (nodes && nodes.length > 1) {
return [...nodes].map(n => n.textContent);
}
return null;
});
expected.forEach(s => {
ok(properties.find(v => v.includes(s)), `Object contents include "${s}"`);
});
}
const { PromiseTestUtils } = ChromeUtils.import( const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm" "resource://testing-common/PromiseTestUtils.jsm"
); );

Просмотреть файл

@ -132,10 +132,7 @@ const previewers = {
RegExp: [ RegExp: [
function({ obj, hooks }, grip) { function({ obj, hooks }, grip) {
const str = DevToolsUtils.callPropertyOnObject(obj, "toString"); const str = ObjectUtils.getRegExpString(obj);
if (typeof str != "string") {
return false;
}
grip.displayString = hooks.createValueGrip(str); grip.displayString = hooks.createValueGrip(str);
return true; return true;
@ -144,7 +141,7 @@ const previewers = {
Date: [ Date: [
function({ obj, hooks }, grip) { function({ obj, hooks }, grip) {
const time = DevToolsUtils.callPropertyOnObject(obj, "getTime"); const time = ObjectUtils.getDateTime(obj);
if (typeof time != "number") { if (typeof time != "number") {
return false; return false;
} }
@ -207,10 +204,7 @@ const previewers = {
Set: [ Set: [
function(objectActor, grip) { function(objectActor, grip) {
const size = DevToolsUtils.getProperty(objectActor.obj, "size"); const size = ObjectUtils.getContainerSize(objectActor.obj);
if (typeof size != "number") {
return false;
}
grip.preview = { grip.preview = {
kind: "ArrayLike", kind: "ArrayLike",
@ -262,10 +256,7 @@ const previewers = {
Map: [ Map: [
function(objectActor, grip) { function(objectActor, grip) {
const size = DevToolsUtils.getProperty(objectActor.obj, "size"); const size = ObjectUtils.getContainerSize(objectActor.obj);
if (typeof size != "number") {
return false;
}
grip.preview = { grip.preview = {
kind: "MapLike", kind: "MapLike",
@ -568,21 +559,11 @@ previewers.Object = [
case "SyntaxError": case "SyntaxError":
case "TypeError": case "TypeError":
case "URIError": case "URIError":
const name = DevToolsUtils.getProperty(obj, "name"); grip.preview = { kind: "Error" };
const msg = DevToolsUtils.getProperty(obj, "message"); const properties = ObjectUtils.getErrorProperties(obj);
const stack = DevToolsUtils.getProperty(obj, "stack"); Object.keys(properties).forEach(p => {
const fileName = DevToolsUtils.getProperty(obj, "fileName"); grip.preview[p] = hooks.createValueGrip(properties[p]);
const lineNumber = DevToolsUtils.getProperty(obj, "lineNumber"); });
const columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
grip.preview = {
kind: "Error",
name: hooks.createValueGrip(name),
message: hooks.createValueGrip(msg),
stack: hooks.createValueGrip(stack),
fileName: hooks.createValueGrip(fileName),
lineNumber: hooks.createValueGrip(lineNumber),
columnNumber: hooks.createValueGrip(columnNumber),
};
return true; return true;
default: default:
return false; return false;

Просмотреть файл

@ -198,13 +198,17 @@ function getArrayLength(object) {
throw new Error("Expected an array, got a " + object.class); throw new Error("Expected an array, got a " + object.class);
} }
// Real arrays have a reliable `length` own property. When replaying, always // Real arrays have a reliable `length` own property.
// get the length property, as we can't invoke getters on the proxy returned if (object.class === "Array") {
// by unsafeDereference().
if (object.class === "Array" || isReplaying) {
return DevToolsUtils.getProperty(object, "length"); return DevToolsUtils.getProperty(object, "length");
} }
// When replaying, we use a special API to get typed array lengths. We can't
// invoke getters on the proxy returned by unsafeDereference().
if (isReplaying) {
return object.getTypedArrayLength();
}
// For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length` // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
// getter could be shadowed by an own property, and `getOwnPropertyNames` is // getter could be shadowed by an own property, and `getOwnPropertyNames` is
// unnecessarily slow. Obtain the `length` getter safely and call it manually. // unnecessarily slow. Obtain the `length` getter safely and call it manually.
@ -213,6 +217,25 @@ function getArrayLength(object) {
return getter.call(object.unsafeDereference()); return getter.call(object.unsafeDereference());
} }
/**
* Returns the number of elements in a Set or Map.
*
* @param object Debugger.Object
* The debuggee object of the Set or Map.
* @return Number
*/
function getContainerSize(object) {
if (object.class != "Set" && object.class != "Map") {
throw new Error(`Expected a set/map, got a ${object.class}`);
}
if (isReplaying) {
return object.getContainerSize();
}
return DevToolsUtils.getProperty(object, "size");
}
/** /**
* Returns true if the parameter is suitable to be an array index. * Returns true if the parameter is suitable to be an array index.
* *
@ -256,6 +279,41 @@ function getStorageLength(object) {
return DevToolsUtils.getProperty(object, "length"); return DevToolsUtils.getProperty(object, "length");
} }
// Get the string representation of a Debugger.Object for a RegExp.
function getRegExpString(object) {
if (isReplaying) {
return object.getRegExpString();
}
return DevToolsUtils.callPropertyOnObject(object, "toString");
}
// Get the time associated with a Debugger.Object for a Date.
function getDateTime(object) {
if (isReplaying) {
return object.getDateTime();
}
return DevToolsUtils.callPropertyOnObject(object, "getTime");
}
// Get the properties of a Debugger.Object for an Error which are needed to
// preview the object.
function getErrorProperties(object) {
if (isReplaying) {
return object.getErrorProperties();
}
return {
name: DevToolsUtils.getProperty(object, "name"),
message: DevToolsUtils.getProperty(object, "message"),
stack: DevToolsUtils.getProperty(object, "stack"),
fileName: DevToolsUtils.getProperty(object, "fileName"),
lineNumber: DevToolsUtils.getProperty(object, "lineNumber"),
columnNumber: DevToolsUtils.getProperty(object, "columnNumber"),
};
}
module.exports = { module.exports = {
getPromiseState, getPromiseState,
makeDebuggeeValueIfNeeded, makeDebuggeeValueIfNeeded,
@ -267,5 +325,9 @@ module.exports = {
isStorage, isStorage,
getArrayLength, getArrayLength,
getStorageLength, getStorageLength,
getContainerSize,
isArrayIndex, isArrayIndex,
getRegExpString,
getDateTime,
getErrorProperties,
}; };

Просмотреть файл

@ -1482,14 +1482,10 @@ async function evaluateLogpoint({ point, text, condition, callback }) {
return { kind: "hitLogpoint", text, condition, skipPauseData }; return { kind: "hitLogpoint", text, condition, skipPauseData };
}, },
onFinished(child, { pauseData, result, resultData, restoredSnapshot }) { onFinished(child, { pauseData, result, resultData, restoredSnapshot }) {
if (restoredSnapshot) { if (restoredSnapshot && !skipPauseData) {
if (!skipPauseData) { // Gathering pause data sometimes triggers a snapshot restore.
// Gathering pause data sometimes triggers a snapshot restore. skipPauseData = true;
skipPauseData = true; sendAsyncManifest(manifest);
sendAsyncManifest(manifest);
} else {
callback(point, ["Recording divergence evaluating logpoint"]);
}
} else { } else {
if (result) { if (result) {
if (!skipPauseData) { if (!skipPauseData) {

Просмотреть файл

@ -26,12 +26,6 @@ ChromeUtils.defineModuleGetter(
"resource://devtools/shared/execution-point-utils.js" "resource://devtools/shared/execution-point-utils.js"
); );
loader.lazyRequireGetter(
this,
"ReplayInspector",
"devtools/server/actors/replay/inspector"
);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// ReplayDebugger // ReplayDebugger
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -125,8 +119,7 @@ ReplayPool.prototype = {
} }
this.getObject(data.id)._preview = { this.getObject(data.id)._preview = {
...preview, ...preview,
properties: mapify(preview.properties), enumerableOwnProperties: mapify(preview.enumerableOwnProperties),
callResults: mapify(preview.callResults),
}; };
} }
@ -1114,12 +1107,6 @@ ReplayDebuggerFrame.prototype = {
// ReplayDebuggerObject // ReplayDebuggerObject
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// See replay.js
const PropertyLevels = {
BASIC: 1,
FULL: 2,
};
function ReplayDebuggerObject(pool, data) { function ReplayDebuggerObject(pool, data) {
this._dbg = pool.dbg; this._dbg = pool.dbg;
this._pool = pool; this._pool = pool;
@ -1186,30 +1173,18 @@ ReplayDebuggerObject.prototype = {
}, },
unsafeDereference() { unsafeDereference() {
if (this.class == "Array") { // Direct access to the referent is not currently available.
// ReplayInspector converts arrays to objects in this process, which we return null;
// don't want to happen.
return null;
}
return ReplayInspector.wrapObject(this);
}, },
getOwnPropertyNames() { getOwnPropertyNames() {
if (this._preview && this._preview.level >= PropertyLevels.FULL) {
// The preview will include all properties of the object.
return this.getEnumerableOwnPropertyNamesForPreview();
}
this._ensureProperties(); this._ensureProperties();
return [...this._properties.keys()]; return [...this._properties.keys()];
}, },
getEnumerableOwnPropertyNamesForPreview() { getEnumerableOwnPropertyNamesForPreview() {
if (this._preview && this._preview.level >= PropertyLevels.BASIC) { if (this._preview && this._preview.enumerableOwnProperties) {
if (!this._preview.properties) { return [...this._preview.enumerableOwnProperties.keys()];
return [];
}
return [...this._preview.properties.keys()];
} }
return this.getOwnPropertyNames(); return this.getOwnPropertyNames();
}, },
@ -1228,10 +1203,20 @@ ReplayDebuggerObject.prototype = {
getOwnPropertyDescriptor(name) { getOwnPropertyDescriptor(name) {
name = name.toString(); name = name.toString();
if (this._preview && this._preview.properties) { if (this._preview) {
const desc = this._preview.properties.get(name); if (this._preview.enumerableOwnProperties) {
if (desc || this._preview.level == PropertyLevels.FULL) { const desc = this._preview.enumerableOwnProperties.get(name);
return this._convertPropertyDescriptor(desc); if (desc) {
return this._convertPropertyDescriptor(desc);
}
}
if (name == "length") {
return this._convertPropertyDescriptor(this._preview.lengthProperty);
}
if (name == "displayName") {
return this._convertPropertyDescriptor(
this._preview.displayNameProperty
);
} }
} }
this._ensureProperties(); this._ensureProperties();
@ -1245,7 +1230,7 @@ ReplayDebuggerObject.prototype = {
return; return;
} }
const id = this._data.id; const id = this._data.id;
const { properties } = this._dbg._sendRequestAllowDiverge( const properties = this._dbg._sendRequestAllowDiverge(
{ type: "getObjectProperties", id }, { type: "getObjectProperties", id },
[] []
); );
@ -1294,19 +1279,6 @@ ReplayDebuggerObject.prototype = {
}); });
}, },
replayHasCallResult(name) {
return (
this._preview &&
this._preview.callResults &&
this._preview.callResults.has(name)
);
},
replayCallResult(name) {
const value = this._preview.callResults.get(name);
return this._pool.convertValue(value);
},
unwrap() { unwrap() {
if (!this.isProxy) { if (!this.isProxy) {
return this; return this;
@ -1348,10 +1320,7 @@ ReplayDebuggerObject.prototype = {
}, },
apply(thisv, args) { apply(thisv, args) {
if (this._pool != this._dbg._pool) { assert(this._pool == this._dbg._pool);
return undefined;
}
thisv = this._dbg._convertValueForChild(thisv); thisv = this._dbg._convertValueForChild(thisv);
args = (args || []).map(v => this._dbg._convertValueForChild(v)); args = (args || []).map(v => this._dbg._convertValueForChild(v));
@ -1393,20 +1362,25 @@ ReplayDebuggerObject.prototype = {
return this._data.typedArrayLength; return this._data.typedArrayLength;
}, },
makeDebuggeeValue(obj) { getContainerSize() {
if (obj instanceof ReplayDebuggerObject) { return this._data.containerSize;
return obj;
}
const rv = ReplayInspector.unwrapObject(obj);
if (rv) {
return rv;
}
ThrowError("Can't make debuggee value");
return null; // For eslint
}, },
replayIsInstance(name) { getRegExpString() {
return this._data.isInstance == name; return this._data.regExpString;
},
getDateTime() {
return this._data.dateTime;
},
getErrorProperties() {
return this._data.errorProperties;
},
makeDebuggeeValue(obj) {
assert(obj instanceof ReplayDebuggerObject);
return obj;
}, },
preventExtensions: NotAllowed, preventExtensions: NotAllowed,

Просмотреть файл

@ -76,6 +76,16 @@ const ReplayInspector = {
); );
}, },
// Create the CSSRule object to bind for other server users.
createCSSRule(rule) {
return {
...rule,
isInstance(node) {
return gFixedProxy.CSSRule.isInstance(node);
},
};
},
wrapRequireHook(requireHook) { wrapRequireHook(requireHook) {
return (id, require) => { return (id, require) => {
const rv = requireHook(id, require); const rv = requireHook(id, require);
@ -99,31 +109,8 @@ const ReplayInspector = {
getDebuggerObject(node) { getDebuggerObject(node) {
return unwrapValue(node); return unwrapValue(node);
}, },
// For use by ReplayDebugger.
wrapObject,
unwrapObject(obj) {
return proxyMap.get(obj);
},
}; };
// Objects we need to override isInstance for.
const gOverrideIsInstance = ["CSSRule", "Event"];
for (const name of gOverrideIsInstance) {
ReplayInspector[`create${name}`] = original => ({
...original,
isInstance(obj) {
const unwrapped = proxyMap.get(obj);
if (!unwrapped) {
return original.isInstance(obj);
}
assert(unwrapped instanceof ReplayDebugger.Object);
return unwrapped.replayIsInstance(name);
},
});
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Require Substitutions // Require Substitutions
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -297,7 +284,6 @@ function unwrapValue(value) {
} }
function getObjectProperty(obj, name) { function getObjectProperty(obj, name) {
assert(obj._pool == dbg()._pool);
const rv = dbg()._sendRequestAllowDiverge({ const rv = dbg()._sendRequestAllowDiverge({
type: "getObjectPropertyValue", type: "getObjectPropertyValue",
id: obj._data.id, id: obj._data.id,
@ -307,7 +293,6 @@ function getObjectProperty(obj, name) {
} }
function setObjectProperty(obj, name, value) { function setObjectProperty(obj, name, value) {
assert(obj._pool == dbg()._pool);
const rv = dbg()._sendRequestAllowDiverge({ const rv = dbg()._sendRequestAllowDiverge({
type: "setObjectPropertyValue", type: "setObjectPropertyValue",
id: obj._data.id, id: obj._data.id,

Просмотреть файл

@ -359,12 +359,12 @@ Services.obs.addObserver(
// Message arguments are preserved as debuggee values. // Message arguments are preserved as debuggee values.
if (apiMessage.arguments) { if (apiMessage.arguments) {
contents.arguments = apiMessage.arguments.map(v => { contents.arguments = apiMessage.arguments.map(v => {
return makeConvertedDebuggeeValue(v); return convertValue(makeDebuggeeValue(v));
}); });
contents.argumentsData = new PreviewedObjects(); contents.argumentsData = new PreviewedObjects();
contents.arguments.forEach(v => contents.arguments.forEach(v =>
contents.argumentsData.addValue(v, PropertyLevels.FULL) contents.argumentsData.addValue(v, true)
); );
ClearPausedState(); ClearPausedState();
@ -549,35 +549,36 @@ function findAllScriptHits(script, frameIndex, offsets, startpoint, endpoint) {
return allHits; return allHits;
} }
function findChangeFrames(checkpoint, which, kind) { function findChangeFrames(checkpoint, which, kind, frameIndex, maybeScript) {
const hits = RecordReplayControl.findChangeFrames(checkpoint, which); const hits = RecordReplayControl.findChangeFrames(checkpoint, which);
return hits.map(({ script, progress, frameIndex }) => ({ return hits
checkpoint, .filter(
progress, hit =>
position: { kind, script, frameIndex }, hit.frameIndex == frameIndex &&
})); (!maybeScript || hit.script == maybeScript)
)
.map(({ script, progress }) => ({
checkpoint,
progress,
position: { kind, script, frameIndex },
}));
} }
function findFrameSteps({ targetPoint, breakpointOffsets }) { function findFrameSteps({ targetPoint, breakpointOffsets }) {
const { const {
checkpoint, checkpoint,
position: { script: targetScript, frameIndex: targetIndex }, position: { script, frameIndex: targetIndex },
} = targetPoint; } = targetPoint;
const potentialStepsFilter = point => {
const { frameIndex, script } = point.position;
return frameIndex == targetIndex && script == targetScript;
};
// Find the entry point of the frame whose steps contain |targetPoint|. // Find the entry point of the frame whose steps contain |targetPoint|.
let entryPoint; let entryPoint;
if (targetPoint.position.kind == "EnterFrame") { if (targetPoint.position.kind == "EnterFrame") {
entryPoint = targetPoint; entryPoint = targetPoint;
} else { } else {
const entryHits = [ const entryHits = [
...findChangeFrames(checkpoint, 0, "EnterFrame"), ...findChangeFrames(checkpoint, 0, "EnterFrame", targetIndex, script),
...findChangeFrames(checkpoint, 2, "EnterFrame"), ...findChangeFrames(checkpoint, 2, "EnterFrame", targetIndex, script),
].filter(potentialStepsFilter); ];
// Find the last frame entry or resume for the frame's script preceding the // Find the last frame entry or resume for the frame's script preceding the
// target point. Since frames do not span checkpoints the hit must be in the // target point. Since frames do not span checkpoints the hit must be in the
@ -592,8 +593,12 @@ function findFrameSteps({ targetPoint, breakpointOffsets }) {
} }
// Find the exit point of the frame. // Find the exit point of the frame.
const exitHits = findChangeFrames(checkpoint, 1, "OnPop").filter( const exitHits = findChangeFrames(
potentialStepsFilter checkpoint,
1,
"OnPop",
targetIndex,
script
); );
const exitPoint = findClosestPoint( const exitPoint = findClosestPoint(
exitHits, exitHits,
@ -606,14 +611,17 @@ function findFrameSteps({ targetPoint, breakpointOffsets }) {
// frame index and happen between the entry and exit points. Any EnterFrame // frame index and happen between the entry and exit points. Any EnterFrame
// points for immediate callees of the frame are also included. // points for immediate callees of the frame are also included.
const breakpointHits = findAllScriptHits( const breakpointHits = findAllScriptHits(
targetScript, script,
targetIndex, targetIndex,
breakpointOffsets, breakpointOffsets,
checkpoint, checkpoint,
checkpoint + 1 checkpoint + 1
); );
const enterFrameHits = findChangeFrames(checkpoint, 0, "EnterFrame").filter( const enterFrameHits = findChangeFrames(
point => point.position.frameIndex == targetIndex + 1 checkpoint,
0,
"EnterFrame",
targetIndex + 1
); );
const steps = breakpointHits.concat(enterFrameHits).filter(point => { const steps = breakpointHits.concat(enterFrameHits).filter(point => {
return pointPrecedes(entryPoint, point) && pointPrecedes(point, exitPoint); return pointPrecedes(entryPoint, point) && pointPrecedes(point, exitPoint);
@ -628,9 +636,13 @@ function findFrameSteps({ targetPoint, breakpointOffsets }) {
} }
function findEventFrameEntry({ checkpoint, progress }) { function findEventFrameEntry({ checkpoint, progress }) {
return findChangeFrames(checkpoint, 0, "EnterFrame").filter(point => { const entryHits = findChangeFrames(checkpoint, 0, "EnterFrame", 0);
return point.progress == progress + 1; for (const hit of entryHits) {
})[0]; if (hit.progress == progress + 1) {
return hit;
}
}
return null;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -899,10 +911,6 @@ function makeDebuggeeValue(value) {
return value; return value;
} }
function makeConvertedDebuggeeValue(value) {
return convertValue(makeDebuggeeValue(value));
}
function getDebuggeeValue(value) { function getDebuggeeValue(value) {
if (value && typeof value == "object") { if (value && typeof value == "object") {
assert(value instanceof Debugger.Object); assert(value instanceof Debugger.Object);
@ -1047,10 +1055,10 @@ const gManifestStartHandlers = {
} else { } else {
result = [getDebuggeeValue(rv.throw)]; result = [getDebuggeeValue(rv.throw)];
} }
result = result.map(v => makeConvertedDebuggeeValue(v)); result = result.map(v => convertValue(makeDebuggeeValue(v)));
const resultData = new PreviewedObjects(); const resultData = new PreviewedObjects();
result.forEach(v => resultData.addValue(v, PropertyLevels.FULL)); result.forEach(v => resultData.addValue(v, true));
RecordReplayControl.manifestFinished({ result, resultData, pauseData }); RecordReplayControl.manifestFinished({ result, resultData, pauseData });
}, },
@ -1331,6 +1339,7 @@ function unknownObjectProperties(why) {
]; ];
} }
// eslint-disable-next-line complexity
function getObjectData(id) { function getObjectData(id) {
const object = gPausedObjects.getObject(id); const object = gPausedObjects.getObject(id);
if (object instanceof Debugger.Object) { if (object instanceof Debugger.Object) {
@ -1376,10 +1385,55 @@ function getObjectData(id) {
if (object.errorColumnNumber) { if (object.errorColumnNumber) {
rv.errorColumnNumber = object.errorColumnNumber; rv.errorColumnNumber = object.errorColumnNumber;
} }
if (CSSRule.isInstance(object.unsafeDereference())) {
rv.isInstance = "CSSRule"; const raw = object.unsafeDereference();
} else if (Event.isInstance(object.unsafeDereference())) { switch (object.class) {
rv.isInstance = "Event"; case "Uint8Array":
case "Uint8ClampedArray":
case "Uint16Array":
case "Uint32Array":
case "Int8Array":
case "Int16Array":
case "Int32Array":
case "Float32Array":
case "Float64Array": {
const typedProto = Object.getPrototypeOf(Uint8Array.prototype);
const { get } = Object.getOwnPropertyDescriptor(typedProto, "length");
rv.typedArrayLength = get.call(raw);
break;
}
case "Set": {
const { get } = Object.getOwnPropertyDescriptor(Set.prototype, "size");
rv.containerSize = get.call(raw);
break;
}
case "Map": {
const { get } = Object.getOwnPropertyDescriptor(Map.prototype, "size");
rv.containerSize = get.call(raw);
break;
}
case "RegExp":
rv.regExpString = RegExp.prototype.toString.call(raw);
break;
case "Date":
rv.dateTime = Date.prototype.getTime.call(raw);
break;
case "Error":
case "EvalError":
case "RangeError":
case "ReferenceError":
case "SyntaxError":
case "TypeError":
case "URIError":
rv.errorProperties = {
name: raw.name,
message: raw.message,
stack: raw.stack,
fileName: raw.fileName,
lineNumber: raw.lineNumber,
columnNumber: raw.columnNumber,
};
break;
} }
return rv; return rv;
} }
@ -1397,21 +1451,7 @@ function getObjectData(id) {
throwError(`Unknown object kind: ${object}`); throwError(`Unknown object kind: ${object}`);
} }
// Return whether to avoid operating on an object due to the likelihood of a
// recording divergence or other bad behavior.
function isBlacklisted(object) {
// Enumerate a Storage object's properties requires the content process to
// synchronously communicate with the UI process, which it can't do.
return object.class == "Storage";
}
function getObjectProperties(object) { function getObjectProperties(object) {
const rv = Object.create(null);
if (isBlacklisted(object)) {
return rv;
}
let names; let names;
try { try {
names = object.getOwnPropertyNames(); names = object.getOwnPropertyNames();
@ -1419,24 +1459,11 @@ function getObjectProperties(object) {
return unknownObjectProperties(e.toString()); return unknownObjectProperties(e.toString());
} }
const rv = Object.create(null);
names.forEach(name => { names.forEach(name => {
// Workaround this test-only getter not reporting exceptions properly.
if (name == "SpecialPowers_wrappedObject") {
return;
}
let desc; let desc;
try { try {
desc = object.getOwnPropertyDescriptor(name); desc = object.getOwnPropertyDescriptor(name);
if (!desc) {
desc = {
name,
desc: {
value: `Unexpected missing property ${name}`,
enumerable: true,
},
};
}
} catch (e) { } catch (e) {
desc = { name, desc: { value: "Unknown: " + e, enumerable: true } }; desc = { name, desc: { value: "Unknown: " + e, enumerable: true } };
} }
@ -1459,24 +1486,24 @@ function getObjectContainerContents(object) {
switch (object.class) { switch (object.class) {
case "Set": { case "Set": {
const iter = Cu.waiveXrays(Set.prototype.values.call(raw)); const iter = Cu.waiveXrays(Set.prototype.values.call(raw));
return [...iter].map(v => makeConvertedDebuggeeValue(v)); return [...iter].map(v => convertValue(makeDebuggeeValue(v)));
} }
case "Map": { case "Map": {
const iter = Cu.waiveXrays(Map.prototype.entries.call(raw)); const iter = Cu.waiveXrays(Map.prototype.entries.call(raw));
return [...iter].map(([k, v]) => [ return [...iter].map(([k, v]) => [
makeConvertedDebuggeeValue(k), convertValue(makeDebuggeeValue(k)),
makeConvertedDebuggeeValue(v), convertValue(makeDebuggeeValue(v)),
]); ]);
} }
case "WeakSet": { case "WeakSet": {
const keys = ChromeUtils.nondeterministicGetWeakSetKeys(raw); const keys = ChromeUtils.nondeterministicGetWeakSetKeys(raw);
return keys.map(k => makeConvertedDebuggeeValue(Cu.waiveXrays(k))); return keys.map(k => convertValue(makeDebuggeeValue(Cu.waiveXrays(k))));
} }
case "WeakMap": { case "WeakMap": {
const keys = ChromeUtils.nondeterministicGetWeakMapKeys(raw); const keys = ChromeUtils.nondeterministicGetWeakMapKeys(raw);
return keys.map(k => [ return keys.map(k => [
makeConvertedDebuggeeValue(k), convertValue(makeDebuggeeValue(k)),
makeConvertedDebuggeeValue(WeakMap.prototype.get.call(raw, k)), convertValue(makeDebuggeeValue(WeakMap.prototype.get.call(raw, k))),
]); ]);
} }
default: default:
@ -1513,13 +1540,6 @@ function getWindow() {
// object. // object.
const OBJECT_PREVIEW_MAX_ITEMS = 10; const OBJECT_PREVIEW_MAX_ITEMS = 10;
// Levels at which property information can be included in previews.
// If not specified, minimal properties are included.
const PropertyLevels = {
BASIC: 1, // Include enough properties to show an inline preview.
FULL: 2, // Include enough properties to allow the object to be expanded.
};
// A collection of objects which we can send up to the server, along with // A collection of objects which we can send up to the server, along with
// property information so that the server can show a preview for the object. // property information so that the server can show a preview for the object.
function PreviewedObjects() { function PreviewedObjects() {
@ -1528,211 +1548,96 @@ function PreviewedObjects() {
} }
PreviewedObjects.prototype = { PreviewedObjects.prototype = {
addValue(value, level) { addValue(value, includeProperties) {
if (value && typeof value == "object" && value.object) { if (value && typeof value == "object" && value.object) {
this.addObject(value.object, level); this.addObject(value.object, includeProperties);
} }
}, },
// eslint-disable-next-line complexity addObject(id, includeProperties) {
addObject(id, level) {
if (!id) { if (!id) {
return; return;
} }
// If includeProperties is set then previewing the object requires knowledge
// of its enumerable properties.
const needObject = !this.objects[id];
const needProperties =
includeProperties &&
(needObject || !this.objects[id].preview.enumerableOwnProperties);
if (!needObject && !needProperties) {
return;
}
const object = gPausedObjects.getObject(id); const object = gPausedObjects.getObject(id);
assert(object instanceof Debugger.Object); assert(object instanceof Debugger.Object);
if (!this.objects[id]) { const properties = getObjectProperties(object);
let ownPropertyNamesCount = 0; const propertyEntries = Object.entries(properties);
try {
ownPropertyNamesCount = object.getOwnPropertyNames().length;
} catch (e) {}
if (needObject) {
this.objects[id] = { this.objects[id] = {
data: getObjectData(id), data: getObjectData(id),
preview: { ownPropertyNamesCount, level }, preview: {
ownPropertyNamesCount: propertyEntries.length,
},
}; };
} else {
const preview = this.objects[id].preview; const preview = this.objects[id].preview;
if ((preview.level | 0) >= (level | 0)) {
return; // Add some properties (if present) which the server might ask for
// even when it isn't interested in the rest of the properties.
if (properties.length) {
preview.lengthProperty = properties.length;
}
if (properties.displayName) {
preview.displayNameProperty = properties.displayName;
} }
preview.level = level;
} }
const { data, preview } = this.objects[id]; if (needProperties) {
const preview = this.objects[id].preview;
// If this is a DOM object identified with isInstance, the previewer might // The server is only interested in enumerable properties, and at most
// need additional properties. // OBJECT_PREVIEW_MAX_ITEMS of them. Limiting the properties we send to
if (level == PropertyLevels.BASIC && data.isInstance) { // only those the server needs avoids having to send the contents of huge
preview.level = level = PropertyLevels.FULL; // objects like Windows, most of which will not be used.
} const enumerableOwnProperties = Object.create(null);
let enumerablePropertyCount = 0;
// Add intrinsic properties that are always included. for (const [name, desc] of propertyEntries) {
switch (object.class) { if (desc.enumerable) {
case "Array": enumerableOwnProperties[name] = desc;
case "Uint8Array": this.addPropertyDescriptor(desc, false);
case "Uint8ClampedArray":
case "Uint16Array":
case "Uint32Array":
case "Int8Array":
case "Int16Array":
case "Int32Array":
case "Float32Array":
case "Float64Array":
this.addObjectPropertyValue(object, "length");
break;
case "Function":
this.addObjectPropertyValue(object, "displayName");
break;
}
if (!level) {
return;
}
const properties = Object.entries(getObjectProperties(object));
// For an inline preview the server is only interested in enumerable
// properties, and at most OBJECT_PREVIEW_MAX_ITEMS of them. Limiting the
// properties we send to only those the server needs avoids having to send
// the contents of huge objects like Windows, most of which will not be
// used. When doing a full property enumeration, include all properties.
let enumerablePropertyCount = 0;
for (const [name, desc] of properties) {
if (level == PropertyLevels.FULL || desc.enumerable) {
this.addObjectProperty(object, name, desc);
if (level == PropertyLevels.BASIC) {
if (++enumerablePropertyCount == OBJECT_PREVIEW_MAX_ITEMS) { if (++enumerablePropertyCount == OBJECT_PREVIEW_MAX_ITEMS) {
break; break;
} }
} }
} }
} preview.enumerableOwnProperties = enumerableOwnProperties;
// The server is interested in at most OBJECT_PREVIEW_MAX_ITEMS items in // The server is interested in at most OBJECT_PREVIEW_MAX_ITEMS items in
// set and map containers. // set and map containers.
let containerContents = getObjectContainerContents(object); const containerContents = getObjectContainerContents(object);
if (containerContents) { if (containerContents) {
if (level == PropertyLevels.BASIC) { preview.containerContents = containerContents.slice(
containerContents = containerContents.slice(
0, 0,
OBJECT_PREVIEW_MAX_ITEMS OBJECT_PREVIEW_MAX_ITEMS
); );
} preview.containerContents.forEach(v => this.addContainerValue(v));
preview.containerContents = containerContents;
preview.containerContents.forEach(v => this.addContainerValue(v));
}
switch (object.class) {
case "RegExp":
this.addObjectCall(object, "toString");
break;
case "Date":
this.addObjectCall(object, "getTime");
break;
case "Set":
case "Map":
this.addObjectPropertyValue(object, "size");
break;
case "Error":
case "EvalError":
case "RangeError":
case "ReferenceError":
case "SyntaxError":
case "TypeError":
case "URIError":
this.addObjectPropertyValue(object, "name");
this.addObjectPropertyValue(object, "message");
this.addObjectPropertyValue(object, "stack");
this.addObjectPropertyValue(object, "fileName");
this.addObjectPropertyValue(object, "lineNumber");
this.addObjectPropertyValue(object, "columnNumber");
break;
}
// Search the prototype chain for getter properties and fill in their values
// if we are getting all properties of the object.
if (level == PropertyLevels.FULL) {
let { proto } = object;
while (proto) {
let names = [];
try {
names = proto.getOwnPropertyNames();
} catch (e) {}
for (const name of names) {
let desc = null;
try {
desc = proto.getOwnPropertyDescriptor(name);
} catch (e) {}
if (desc && desc.get) {
this.addObjectPropertyValue(object, name);
}
}
proto = proto.proto;
} }
} }
}, },
addObjectPropertyValue(object, name) { addPropertyDescriptor(desc, includeProperties) {
try {
const value = makeConvertedDebuggeeValue(
object.unsafeDereference()[name]
);
this.addObjectProperty(object, name, { value, enumerable: true });
} catch (e) {}
},
addObjectProperty(object, name, desc) {
const id = gPausedObjects.getId(object);
const preview = this.objects[id].preview;
if (!preview.properties) {
preview.properties = Object.create(null);
}
if (name in preview.properties) {
return;
}
this.addPropertyDescriptor(desc);
preview.properties[name] = desc;
},
addObjectCall(object, name) {
const id = gPausedObjects.getId(object);
const preview = this.objects[id].preview;
if (!preview.callResults) {
preview.callResults = Object.create(null);
}
if (name in preview.callResults) {
return;
}
try {
const value = makeConvertedDebuggeeValue(
object.unsafeDereference()[name]()
);
this.addValue(value);
preview.callResults[name] = value;
} catch (e) {}
},
addPropertyDescriptor(desc, level) {
if (desc.value) { if (desc.value) {
this.addValue(desc.value, level); this.addValue(desc.value, includeProperties);
} }
if (desc.get) { if (desc.get) {
this.addObject(desc.get, level); this.addObject(desc.get, includeProperties);
} }
if (desc.set) { if (desc.set) {
this.addObject(desc.set, level); this.addObject(desc.set, includeProperties);
} }
}, },
@ -1757,7 +1662,7 @@ PreviewedObjects.prototype = {
const names = getEnvironmentNames(env); const names = getEnvironmentNames(env);
this.environments[id] = { data, names }; this.environments[id] = { data, names };
names.forEach(({ value }) => this.addValue(value, PropertyLevels.BASIC)); names.forEach(({ value }) => this.addValue(value, true));
this.addObject(data.callee); this.addObject(data.callee);
this.addEnvironment(data.parent); this.addEnvironment(data.parent);
@ -1807,14 +1712,14 @@ function getPauseData() {
metadata: script.getOffsetMetadata(dbgFrame.offset), metadata: script.getOffsetMetadata(dbgFrame.offset),
}); });
addScript(frame.script); addScript(frame.script);
rv.addValue(frame.this, PropertyLevels.BASIC); rv.addValue(frame.this, true);
if (frame.arguments) { if (frame.arguments) {
for (const arg of frame.arguments) { for (const arg of frame.arguments) {
rv.addValue(arg, PropertyLevels.BASIC); rv.addValue(arg, true);
} }
} }
rv.addObject(frame.callee, PropertyLevels.NONE); rv.addObject(frame.callee, false);
rv.addEnvironment(frame.environment, PropertyLevels.BASIC); rv.addEnvironment(frame.environment, true);
} }
return rv; return rv;
@ -1903,7 +1808,7 @@ const gRequestHandlers = {
getObjectProperties(request) { getObjectProperties(request) {
divergeFromRecording(); divergeFromRecording();
const object = gPausedObjects.getObject(request.id); const object = gPausedObjects.getObject(request.id);
return { properties: getObjectProperties(object) }; return getObjectProperties(object);
}, },
getObjectContainerContents(request) { getObjectContainerContents(request) {
@ -1915,11 +1820,6 @@ const gRequestHandlers = {
divergeFromRecording(); divergeFromRecording();
const obj = gPausedObjects.getObject(request.id); const obj = gPausedObjects.getObject(request.id);
const thisv = convertValueFromParent(request.thisv); const thisv = convertValueFromParent(request.thisv);
if (thisv instanceof Debugger.Object && isBlacklisted(thisv)) {
return { return: "Can't call method on blacklisted object" };
}
const args = request.args.map(v => convertValueFromParent(v)); const args = request.args.map(v => convertValueFromParent(v));
const rv = obj.apply(thisv, args); const rv = obj.apply(thisv, args);
return convertCompletionValue(rv); return convertCompletionValue(rv);
@ -1993,6 +1893,7 @@ const gRequestHandlers = {
document: getObjectId(makeDebuggeeValue(window.document)), document: getObjectId(makeDebuggeeValue(window.document)),
Services: getObjectId(makeDebuggeeValue(Services)), Services: getObjectId(makeDebuggeeValue(Services)),
InspectorUtils: getObjectId(makeDebuggeeValue(InspectorUtils)), InspectorUtils: getObjectId(makeDebuggeeValue(InspectorUtils)),
CSSRule: getObjectId(makeDebuggeeValue(CSSRule)),
}; };
}, },
@ -2008,13 +1909,9 @@ const gRequestHandlers = {
divergeFromRecording(); divergeFromRecording();
const object = gPausedObjects.getObject(request.id); const object = gPausedObjects.getObject(request.id);
if (isBlacklisted(object)) {
return { return: "Can't get blacklisted object property" };
}
try { try {
const rv = object.unsafeDereference()[request.name]; const rv = object.unsafeDereference()[request.name];
return { return: makeConvertedDebuggeeValue(rv) }; return { return: convertValue(makeDebuggeeValue(rv)) };
} catch (e) { } catch (e) {
return { throw: "" + e }; return { throw: "" + e };
} }

Просмотреть файл

@ -887,12 +887,6 @@ errorOnFlag(exports, "wantVerbose");
// where unsafeDereference will return an opaque security wrapper to the // where unsafeDereference will return an opaque security wrapper to the
// referent. // referent.
function callPropertyOnObject(object, name, ...args) { function callPropertyOnObject(object, name, ...args) {
// When replaying, the result of the call may already be known, which avoids
// having to communicate with the replaying process.
if (isReplaying && args.length == 0 && object.replayHasCallResult(name)) {
return object.replayCallResult(name);
}
// Find the property. // Find the property.
let descriptor; let descriptor;
let proto = object; let proto = object;

Просмотреть файл

@ -340,6 +340,7 @@ exports.globals = {
NodeFilter, NodeFilter,
DOMRect, DOMRect,
Element, Element,
Event,
FileReader, FileReader,
FormData, FormData,
isWorker: false, isWorker: false,
@ -401,19 +402,10 @@ lazyGlobal("indexedDB", () => {
lazyGlobal("isReplaying", () => { lazyGlobal("isReplaying", () => {
return exports.modules.Debugger.recordReplayProcessKind() == "Middleman"; return exports.modules.Debugger.recordReplayProcessKind() == "Middleman";
}); });
lazyGlobal("CSSRule", () => {
// Globals which the ReplayInspector provides an alternate implementation for. if (exports.modules.Debugger.recordReplayProcessKind() == "Middleman") {
const inspectorGlobals = { const ReplayInspector = require("devtools/server/actors/replay/inspector");
CSSRule, return ReplayInspector.createCSSRule(CSSRule);
Event, }
}; return CSSRule;
});
for (const [name, value] of Object.entries(inspectorGlobals)) {
lazyGlobal(name, () => {
if (exports.modules.Debugger.recordReplayProcessKind() == "Middleman") {
const ReplayInspector = require("devtools/server/actors/replay/inspector");
return ReplayInspector[`create${name}`](value);
}
return value;
});
}