mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1424721 - Allow long strings and invisible-to-debugger objects to be stored as global variables. r=nchevobbe
MozReview-Commit-ID: IZFKgror7F6 --HG-- extra : rebase_source : ef2073c1338701a3b1d654975e29d21224481c47
This commit is contained in:
parent
0e5ba4ebd8
commit
c9ff031c33
@ -12,72 +12,61 @@ const TEST_URI = `data:text/html;charset=utf-8,<script>
|
||||
window.bar = { baz: 1 };
|
||||
console.log("foo");
|
||||
console.log("foo", window.bar);
|
||||
console.log(["foo", window.bar, 2]);
|
||||
window.array = ["foo", window.bar, 2];
|
||||
console.log(window.array);
|
||||
window.longString = "foo" + "a".repeat(1e4);
|
||||
console.log(window.longString);
|
||||
</script>`;
|
||||
|
||||
add_task(async function() {
|
||||
let hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
let [msgWithText, msgWithObj, msgNested] =
|
||||
await waitFor(() => findMessages(hud, "foo"));
|
||||
ok(msgWithText && msgWithObj && msgNested, "Three messages should have appeared");
|
||||
|
||||
let text = msgWithText.querySelector(".objectBox-string");
|
||||
let objInMsgWithObj = msgWithObj.querySelector(".objectBox-object");
|
||||
let textInMsgWithObj = msgWithObj.querySelector(".objectBox-string");
|
||||
|
||||
// The third message has an object nested in an array, the array is therefore the top
|
||||
// object, the object is the nested object.
|
||||
let topObjInMsg = msgNested.querySelector(".objectBox-array");
|
||||
let nestedObjInMsg = msgNested.querySelector(".objectBox-object");
|
||||
let messages = await waitFor(() => findMessages(hud, "foo"));
|
||||
is(messages.length, 4, "Four messages should have appeared");
|
||||
let [msgWithText, msgWithObj, msgNested, msgLongStr] = messages;
|
||||
let varIdx = 0;
|
||||
|
||||
info("Check store as global variable is disabled for text only messages");
|
||||
let menuPopup = await openContextMenu(hud, text);
|
||||
let storeMenuItem = menuPopup.querySelector("#console-menu-store");
|
||||
ok(storeMenuItem.disabled, "store as global variable is disabled for text message");
|
||||
await hideContextMenu(hud);
|
||||
await storeAsVariable(hud, msgWithText, "string");
|
||||
|
||||
info("Check store as global variable is disabled for text in complex messages");
|
||||
menuPopup = await openContextMenu(hud, textInMsgWithObj);
|
||||
storeMenuItem = menuPopup.querySelector("#console-menu-store");
|
||||
ok(storeMenuItem.disabled,
|
||||
"store as global variable is disabled for text in complex message");
|
||||
await hideContextMenu(hud);
|
||||
await storeAsVariable(hud, msgWithObj, "string");
|
||||
|
||||
info("Check store as global variable is enabled for objects in complex messages");
|
||||
await storeAsVariable(hud, objInMsgWithObj);
|
||||
|
||||
is(hud.jsterm.getInputValue(), "temp0", "Input was set");
|
||||
|
||||
let executedResult = await hud.jsterm.execute();
|
||||
ok(executedResult.textContent.includes("{ baz: 1 }"),
|
||||
"Correct variable assigned into console");
|
||||
await storeAsVariable(hud, msgWithObj, "object", varIdx++, "window.bar");
|
||||
|
||||
info("Check store as global variable is enabled for top object in nested messages");
|
||||
await storeAsVariable(hud, topObjInMsg);
|
||||
|
||||
is(hud.jsterm.getInputValue(), "temp1", "Input was set");
|
||||
|
||||
executedResult = await hud.jsterm.execute();
|
||||
ok(executedResult.textContent.includes(`[ "foo", {\u2026}, 2 ]`),
|
||||
"Correct variable assigned into console " + executedResult.textContent);
|
||||
await storeAsVariable(hud, msgNested, "array", varIdx++, "window.array");
|
||||
|
||||
info("Check store as global variable is enabled for nested object in nested messages");
|
||||
await storeAsVariable(hud, nestedObjInMsg);
|
||||
await storeAsVariable(hud, msgNested, "object", varIdx++, "window.bar");
|
||||
|
||||
is(hud.jsterm.getInputValue(), "temp2", "Input was set");
|
||||
info("Check store as global variable is enabled for long strings");
|
||||
await storeAsVariable(hud, msgLongStr, "string", varIdx++, "window.longString");
|
||||
|
||||
executedResult = await hud.jsterm.execute();
|
||||
ok(executedResult.textContent.includes("{ baz: 1 }"),
|
||||
"Correct variable assigned into console " + executedResult.textContent);
|
||||
info("Check store as global variable is enabled for invisible-to-debugger objects");
|
||||
let onMessageInvisible = waitForMessage(hud, "foo");
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||
let obj = Cu.Sandbox(Cu.getObjectPrincipal(content), {invisibleToDebugger: true});
|
||||
content.wrappedJSObject.invisibleToDebugger = obj;
|
||||
content.console.log("foo", obj);
|
||||
});
|
||||
let msgInvisible = (await onMessageInvisible).node;
|
||||
await storeAsVariable(hud, msgInvisible, "object", varIdx++, "window.invisibleToDebugger");
|
||||
});
|
||||
|
||||
async function storeAsVariable(hud, element) {
|
||||
info("Check store as global variable is enabled");
|
||||
async function storeAsVariable(hud, msg, type, varIdx, equalTo) {
|
||||
let element = msg.querySelector(".objectBox-" + type);
|
||||
let menuPopup = await openContextMenu(hud, element);
|
||||
let storeMenuItem = menuPopup.querySelector("#console-menu-store");
|
||||
ok(!storeMenuItem.disabled,
|
||||
"store as global variable is enabled for object in complex message");
|
||||
|
||||
if (varIdx == null) {
|
||||
ok(storeMenuItem.disabled, "store as global variable is disabled");
|
||||
await hideContextMenu(hud);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(!storeMenuItem.disabled, "store as global variable is enabled");
|
||||
|
||||
info("Click on store as global variable");
|
||||
let onceInputSet = hud.jsterm.once("set-input-value");
|
||||
@ -88,4 +77,9 @@ async function storeAsVariable(hud, element) {
|
||||
|
||||
info("Wait for context menu to be hidden");
|
||||
await hideContextMenu(hud);
|
||||
|
||||
is(hud.jsterm.getInputValue(), "temp" + varIdx, "Input was set");
|
||||
|
||||
let equal = await hud.jsterm.requestEvaluation("temp" + varIdx + " === " + equalTo);
|
||||
is(equal.result, true, "Correct variable assigned into console.");
|
||||
}
|
||||
|
@ -69,6 +69,10 @@ function ObjectActor(obj, {
|
||||
ObjectActor.prototype = {
|
||||
actorPrefix: "obj",
|
||||
|
||||
rawValue: function () {
|
||||
return this.obj.unsafeDereference();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a grip for this actor for returning in a protocol message.
|
||||
*/
|
||||
@ -2270,6 +2274,10 @@ function LongStringActor(string) {
|
||||
LongStringActor.prototype = {
|
||||
actorPrefix: "longString",
|
||||
|
||||
rawValue: function () {
|
||||
return this.string;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
// Because longStringActors is not a weak map, we won't automatically leave
|
||||
// it so we need to manually leave on destroy so that we don't leak
|
||||
@ -2341,6 +2349,10 @@ function ArrayBufferActor(buffer) {
|
||||
ArrayBufferActor.prototype = {
|
||||
actorPrefix: "arrayBuffer",
|
||||
|
||||
rawValue: function () {
|
||||
return this.buffer;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
},
|
||||
|
||||
|
@ -42,6 +42,10 @@ if (isWorker) {
|
||||
loader.lazyRequireGetter(this, "ContentProcessListener", "devtools/server/actors/webconsole/listeners", true);
|
||||
}
|
||||
|
||||
function isObject(value) {
|
||||
return Object(value) === value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The WebConsoleActor implements capabilities needed for the Web Console
|
||||
* feature.
|
||||
@ -443,8 +447,7 @@ WebConsoleActor.prototype =
|
||||
* Debuggee value for |value|.
|
||||
*/
|
||||
makeDebuggeeValue: function (value, useObjectGlobal) {
|
||||
let isObject = Object(value) === value;
|
||||
if (useObjectGlobal && isObject) {
|
||||
if (useObjectGlobal && isObject(value)) {
|
||||
try {
|
||||
let global = Cu.getGlobalForObject(value);
|
||||
let dbgGlobal = this.dbg.makeGlobalObjectReference(global);
|
||||
@ -1320,17 +1323,25 @@ WebConsoleActor.prototype =
|
||||
let objActor = this.getActorByID(options.bindObjectActor ||
|
||||
options.selectedObjectActor);
|
||||
if (objActor) {
|
||||
let jsObj = objActor.obj.unsafeDereference();
|
||||
// If we use the makeDebuggeeValue method of jsObj's own global, then
|
||||
// we'll get a D.O that sees jsObj as viewed from its own compartment -
|
||||
// that is, without wrappers. The evalWithBindings call will then wrap
|
||||
// jsObj appropriately for the evaluation compartment.
|
||||
let global = Cu.getGlobalForObject(jsObj);
|
||||
let _dbgWindow = dbg.makeGlobalObjectReference(global);
|
||||
bindSelf = dbgWindow.makeDebuggeeValue(jsObj);
|
||||
let jsVal = objActor.rawValue();
|
||||
|
||||
if (options.bindObjectActor) {
|
||||
dbgWindow = _dbgWindow;
|
||||
if (isObject(jsVal)) {
|
||||
// If we use the makeDebuggeeValue method of jsVal's own global, then
|
||||
// we'll get a D.O that sees jsVal as viewed from its own compartment -
|
||||
// that is, without wrappers. The evalWithBindings call will then wrap
|
||||
// jsVal appropriately for the evaluation compartment.
|
||||
bindSelf = dbgWindow.makeDebuggeeValue(jsVal);
|
||||
if (options.bindObjectActor) {
|
||||
let global = Cu.getGlobalForObject(jsVal);
|
||||
try {
|
||||
let _dbgWindow = dbg.makeGlobalObjectReference(global);
|
||||
dbgWindow = _dbgWindow;
|
||||
} catch (err) {
|
||||
// The above will throw if `global` is invisible to debugger.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bindSelf = jsVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user