Bug 886137 - Can't inspect objects in the webconsole, while debugging and inspecting; r=past

This commit is contained in:
Mihai Sucan 2013-06-28 19:16:18 +03:00
parent a8a59fd05f
commit fff43b3fcf
3 changed files with 177 additions and 60 deletions

View File

@ -141,6 +141,7 @@ MOCHITEST_BROWSER_FILES = \
browser_console_filters.js \
browser_console_dead_objects.js \
browser_console_iframe_messages.js \
browser_console_variables_view_while_debugging_and_inspecting.js \
head.js \
$(NULL)

View File

@ -0,0 +1,127 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that makes sure web console eval works while the js debugger paused the
// page, and while the inspector is active. See bug 886137.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-eval-in-stackframe.html";
let gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController,
gStackframes, gVariablesView;
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
gWebConsole = hud;
gJSTerm = hud.jsterm;
info("openDebugger");
openDebugger().then(debuggerOpened);
}
function debuggerOpened(aResult)
{
gDebuggerWin = aResult.panelWin;
gDebuggerController = gDebuggerWin.DebuggerController;
gThread = gDebuggerController.activeThread;
gStackframes = gDebuggerController.StackFrames;
openInspector(inspectorOpened);
}
function inspectorOpened(aPanel)
{
gThread.addOneTimeListener("framesadded", onFramesAdded);
info("firstCall()");
content.wrappedJSObject.firstCall();
}
function onFramesAdded()
{
info("onFramesAdded");
openConsole(null, () => gJSTerm.execute("fooObj", onExecuteFooObj));
}
function onExecuteFooObj()
{
let msg = gWebConsole.outputNode.querySelector(".webconsole-msg-output");
ok(msg, "output message found");
isnot(msg.textContent.indexOf("[object Object]"), -1, "message text check");
gJSTerm.once("variablesview-fetched", onFooObjFetch);
EventUtils.synthesizeMouse(msg, 2, 2, {}, gWebConsole.iframeWindow);
}
function onFooObjFetch(aEvent, aVar)
{
gVariablesView = aVar._variablesView;
ok(gVariablesView, "variables view object");
findVariableViewProperties(aVar, [
{ name: "testProp2", value: "testValue2" },
{ name: "testProp", value: "testValue", dontMatch: true },
], { webconsole: gWebConsole }).then(onTestPropFound);
}
function onTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the |testProp2| property in the variables view");
// Check that property value updates work and that jsterm functions can be
// used.
updateVariablesViewProperty({
property: prop,
field: "value",
string: "document.title + foo2 + $('p')",
webconsole: gWebConsole,
callback: onFooObjFetchAfterUpdate,
});
}
function onFooObjFetchAfterUpdate(aEvent, aVar)
{
info("onFooObjFetchAfterUpdate");
let para = content.wrappedJSObject.document.querySelector("p");
let expectedValue = content.document.title + "foo2SecondCall" + para;
findVariableViewProperties(aVar, [
{ name: "testProp2", value: expectedValue },
], { webconsole: gWebConsole }).then(onUpdatedTestPropFound);
}
function onUpdatedTestPropFound(aResults)
{
let prop = aResults[0].matchedProp;
ok(prop, "matched the updated |testProp2| property value");
// Check that testProp2 was updated.
gJSTerm.execute("fooObj.testProp2", onExecuteFooObjTestProp2);
}
function onExecuteFooObjTestProp2()
{
let para = content.wrappedJSObject.document.querySelector("p");
let expected = content.document.title + "foo2SecondCall" + para;
isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
"fooObj.testProp2 is correct");
gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController =
gStackframes = gVariablesView = null;
finishTest();
}

View File

@ -132,14 +132,6 @@ WebConsoleActor.prototype =
*/
_prefs: null,
/**
* Tells the current inner ID of |this.window|. When the page is navigated, we
* need to recreate the jsterm helpers.
* @private
* @type number
*/
_globalWindowId: 0,
/**
* Holds a map between inner window IDs and Debugger.Objects for the window
* objects.
@ -157,16 +149,6 @@ WebConsoleActor.prototype =
*/
_netEvents: null,
/**
* Object that holds the JSTerm API, the helper functions, for the default
* window object.
*
* @see this._getJSTermHelpers()
* @private
* @type object
*/
_jstermHelpers: null,
/**
* A cache of prototype chains for objects that have received a
* prototypeAndProperties request.
@ -266,10 +248,8 @@ WebConsoleActor.prototype =
this._netEvents.clear();
this._protoChains.clear();
this._dbgGlobals.clear();
this._jstermHelpers = null;
this.dbg.enabled = false;
this.dbg = null;
this._globalWindowId = 0;
this.conn = this._window = null;
},
@ -741,8 +721,14 @@ WebConsoleActor.prototype =
*/
_getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerGlobal)
{
let helpers = Object.create(this);
helpers.sandbox = Object.create(null);
let helpers = {
window: this.window,
chromeWindow: this.chromeWindow.bind(this),
makeDebuggeeValue: aDebuggerGlobal.makeDebuggeeValue.bind(aDebuggerGlobal),
createValueGrip: this.createValueGrip.bind(this),
sandbox: Object.create(null),
helperResult: null,
};
JSTermHelpers(helpers);
// Make sure the helpers can be used during eval.
@ -816,8 +802,9 @@ WebConsoleActor.prototype =
aString = "help()";
}
// Find the Debugger.Object of the given ObjectActor. This is used as
// a binding during eval: |_self|.
let bindSelf = null;
if (aOptions.bindObjectActor) {
let objActor = this.getActorByID(aOptions.bindObjectActor);
if (objActor) {
@ -825,6 +812,7 @@ WebConsoleActor.prototype =
}
}
// Find the Debugger.Frame of the given FrameActor.
let frame = null, frameActor = null;
if (aOptions.frameActor) {
frameActor = this.conn.getActor(aOptions.frameActor);
@ -837,20 +825,49 @@ WebConsoleActor.prototype =
}
}
let dbg = this.dbg;
let dbgWindow = null;
let helpers = null;
let found$ = false, found$$ = false;
// Determine which debugger to use, depending on the presence of the
// stackframe.
// This helps with avoid having bindings from a different Debugger. The
// Debugger.Frame comes from the jsdebugger's Debugger instance.
let dbg = this.dbg;
let dbgWindow = this._getDebuggerGlobal(this.window);
if (frame) {
// Avoid having bindings from a different Debugger. The Debugger.Frame
// comes from the jsdebugger's Debugger instance.
dbg = frameActor.threadActor.dbg;
dbgWindow = dbg.addDebuggee(this.window);
helpers = this._getJSTermHelpers(dbgWindow);
}
// If we have an object to bind to |_self| we need to determine the
// global of the given JavaScript object.
if (bindSelf) {
let jsObj = bindSelf.unsafeDereference();
let global = Cu.getGlobalForObject(jsObj);
// Get the Debugger.Object for the new global.
if (global != this.window) {
dbgWindow = dbg.addDebuggee(global);
// Remove the debuggee only if the Debugger instance belongs to the
// console actor, to avoid breaking the ThreadActor that owns the
// Debugger object.
if (dbg == this.dbg) {
dbg.removeDebuggee(global);
}
}
bindSelf = dbgWindow.makeDebuggeeValue(jsObj);
}
// Get the JSTerm helpers for the given debugger window.
let helpers = this._getJSTermHelpers(dbgWindow);
let bindings = helpers.sandbox;
if (bindSelf) {
bindings._self = bindSelf;
}
// Check if the Debugger.Frame or Debugger.Object for the global include
// $ or $$. We will not overwrite these functions with the jsterm helpers.
let found$ = false, found$$ = false;
if (frame) {
let env = frame.environment;
if (env) {
found$ = !!env.find("$");
@ -858,39 +875,10 @@ WebConsoleActor.prototype =
}
}
else {
// Use the Web Console debugger object.
dbgWindow = this._getDebuggerGlobal(this.window);
let windowId = WebConsoleUtils.getInnerWindowId(this.window);
if (this._globalWindowId != windowId) {
this._jstermHelpers = null;
this._globalWindowId = windowId;
}
if (!this._jstermHelpers) {
this._jstermHelpers = this._getJSTermHelpers(dbgWindow);
}
helpers = this._jstermHelpers;
found$ = !!dbgWindow.getOwnPropertyDescriptor("$");
found$$ = !!dbgWindow.getOwnPropertyDescriptor("$$");
}
let bindings = helpers.sandbox;
if (bindSelf) {
// Determine the global of the given JavaScript object.
let jsObj = bindSelf.unsafeDereference();
let global = Cu.getGlobalForObject(jsObj);
if (global != this.window) {
dbgWindow = dbg.addDebuggee(global);
if (dbg == this.dbg) {
dbg.removeDebuggee(global);
}
}
bindings._self = dbgWindow.makeDebuggeeValue(jsObj);
}
let $ = null, $$ = null;
if (found$) {
$ = bindings.$;
@ -901,6 +889,7 @@ WebConsoleActor.prototype =
delete bindings.$$;
}
// Ready to evaluate the string.
helpers.evalInput = aString;
let result;