зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0549668bb1
Коммит
4174e65ff5
|
@ -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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче