Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
});
add_task(
threadFrontTest(async ({ commands, threadFront, debuggee }) => {
const packet = await executeOnNextTickAndWaitForPause(
() => evalCode(debuggee),
threadFront
);
const arg1 = packet.frame.arguments[0];
Assert.equal(arg1.class, "Object");
const objectFront = threadFront.pauseGrip(arg1);
const obj1 = (
await objectFront.getPropertyValue("obj1", null)
).value.return.getGrip();
const obj2 = (
await objectFront.getPropertyValue("obj2", null)
).value.return.getGrip();
info(`Retrieve "context" function reference`);
const context = (await objectFront.getPropertyValue("context", null)).value
.return;
info(`Retrieve "sum" function reference`);
const sum = (await objectFront.getPropertyValue("sum", null)).value.return;
info(`Retrieve "error" function reference`);
const error = (await objectFront.getPropertyValue("error", null)).value
.return;
const notCallable = (
await objectFront.getPropertyValue("notCallable", null)
).value.return;
assert_response(await context.apply(obj1, [obj1]), {
return: "correct context",
});
assert_response(await context.apply(obj2, [obj2]), {
return: "correct context",
});
assert_response(await context.apply(obj1, [obj2]), {
return: "wrong context",
});
assert_response(await context.apply(obj2, [obj1]), {
return: "wrong context",
});
// eslint-disable-next-line no-useless-call
assert_response(await sum.apply(null, [1, 2, 3, 4, 5, 6, 7]), {
return: 1 + 2 + 3 + 4 + 5 + 6 + 7,
});
// eslint-disable-next-line no-useless-call
assert_response(await error.apply(null, []), {
throw: "an error",
});
try {
await notCallable.apply(obj1, []);
Assert.ok(false, "expected exception");
} catch (err) {
Assert.ok(!!err.message.match(/debugee object is not callable/));
}
await resume(threadFront);
// In order to cover pausing from getPropertyValue we have to first resume
// as pausing while already paused will be ignored.
// So we have to have the pausingProp in a global object and access it while not paused.
const { result: secondObjectFront } =
await commands.scriptCommand.execute("obj");
const onPropertyResumed = secondObjectFront.getPropertyValue(
"pausingProp",
null
);
// Ensure that we actually paused at the `debugger;` line.
const packet2 = await waitForPause(threadFront);
Assert.equal(packet2.frame.where.line, 18);
Assert.equal(packet2.frame.where.column, 8);
await threadFront.resume();
await onPropertyResumed;
})
);
function evalCode(debuggee) {
debuggee.eval(
// These arguments are tested.
// eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
);
debuggee.eval(`
stopMe({
obj1: {},
obj2: {},
context(arg) {
return this === arg ? "correct context" : "wrong context";
},
sum(...parts) {
return parts.reduce((acc, v) => acc + v, 0);
},
error() {
throw "an error";
},
notCallable: {},
});
var obj = {
get pausingProp() {
debugger;
},
};
`);
}
function assert_response({ value }, expected) {
assert_completion(value, expected);
}
function assert_completion(value, expected) {
if (expected && "return" in expected) {
assert_value(value.return, expected.return);
}
if (expected && "throw" in expected) {
assert_value(value.throw, expected.throw);
}
if (!expected) {
assert_value(value, expected);
}
}
function assert_value(actual, expected) {
Assert.equal(typeof actual, typeof expected);
if (typeof expected === "object") {
// Note: We aren't using deepEqual here because we're only doing a cursory
// check of a few properties, not a full comparison of the result, since
// the full outputs includes stuff like preview info that we don't need.
for (const key of Object.keys(expected)) {
assert_value(actual[key], expected[key]);
}
} else {
Assert.equal(actual, expected);
}
}