1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_URL = EXAMPLE_URL + "doc_closures.html";
// Test that inspecting a closure works as expected.
function test() {
let gPanel, gTab, gDebuggee, gDebugger;
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gDebuggee.gRecurseLimit = 2;
waitForSourceShown(gPanel, ".html")
.then(testClosure)
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
function testClosure() {
// Spin the event loop before causing the debuggee to pause, to allow
// this function to return first.
executeSoon(() => {
EventUtils.sendMouseEvent({ type: "click" },
gDebuggee.document.querySelector("button"),
gDebuggee);
});
return waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES).then(() => {
let gVars = gDebugger.DebuggerView.Variables;
let localScope = gVars.getScopeAtIndex(0);
let localNodes = localScope.target.querySelector(".variables-view-element-details").childNodes;
is(localNodes[4].querySelector(".name").getAttribute("value"), "person",
"Should have the right property name for |person|.");
is(localNodes[4].querySelector(".value").getAttribute("value"), "Object",
"Should have the right property value for |person|.");
// Expand the 'person' tree node. This causes its properties to be
// retrieved and displayed.
let personNode = gVars.getItemForNode(localNodes[4]);
let personFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES);
personNode.expand();
return personFetched.then(() => {
is(personNode.expanded, true,
"|person| should be expanded at this point.");
is(personNode.get("getName").target.querySelector(".name")
.getAttribute("value"), "getName",
"Should have the right property name for 'getName' in person.");
is(personNode.get("getName").target.querySelector(".value")
.getAttribute("value"), "_pfactory/<.getName()",
"'getName' in person should have the right value.");
is(personNode.get("getFoo").target.querySelector(".name")
.getAttribute("value"), "getFoo",
"Should have the right property name for 'getFoo' in person.");
is(personNode.get("getFoo").target.querySelector(".value")
.getAttribute("value"), "_pfactory/<.getFoo()",
"'getFoo' in person should have the right value.");
// Expand the function nodes. This causes their properties to be
// retrieved and displayed.
let getFooNode = personNode.get("getFoo");
let getNameNode = personNode.get("getName");
let funcsFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES, 2);
let funcClosuresFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES, 2);
getFooNode.expand();
getNameNode.expand();
return funcsFetched.then(() => {
is(getFooNode.expanded, true,
"|person.getFoo| should be expanded at this point.");
is(getNameNode.expanded, true,
"|person.getName| should be expanded at this point.");
is(getFooNode.get("<Closure>").target.querySelector(".name")
.getAttribute("value"), "<Closure>",
"Found the closure node for getFoo.");
is(getFooNode.get("<Closure>").target.querySelector(".value")
.getAttribute("value"), "",
"The closure node has no value for getFoo.");
is(getNameNode.get("<Closure>").target.querySelector(".name")
.getAttribute("value"), "<Closure>",
"Found the closure node for getName.");
is(getNameNode.get("<Closure>").target.querySelector(".value")
.getAttribute("value"), "",
"The closure node has no value for getName.");
// Expand the closure nodes. This causes their environments to be
// retrieved and displayed.
let getFooClosure = getFooNode.get("<Closure>");
let getNameClosure = getNameNode.get("<Closure>");
getFooClosure.expand();
getNameClosure.expand();
return funcClosuresFetched.then(() => {
is(getFooClosure.expanded, true,
"|person.getFoo| closure should be expanded at this point.");
is(getNameClosure.expanded, true,
"|person.getName| closure should be expanded at this point.");
is(getFooClosure.get("Function scope [_pfactory]").target.querySelector(".name")
.getAttribute("value"), "Function scope [_pfactory]",
"Found the function scope node for the getFoo closure.");
is(getFooClosure.get("Function scope [_pfactory]").target.querySelector(".value")
.getAttribute("value"), "",
"The function scope node has no value for the getFoo closure.");
is(getNameClosure.get("Function scope [_pfactory]").target.querySelector(".name")
.getAttribute("value"), "Function scope [_pfactory]",
"Found the function scope node for the getName closure.");
is(getNameClosure.get("Function scope [_pfactory]").target.querySelector(".value")
.getAttribute("value"), "",
"The function scope node has no value for the getName closure.");
// Expand the scope nodes.
let getFooInnerScope = getFooClosure.get("Function scope [_pfactory]");
let getNameInnerScope = getNameClosure.get("Function scope [_pfactory]");
let innerFuncsFetched = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES, 2);
getFooInnerScope.expand();
getNameInnerScope.expand();
return funcsFetched.then(() => {
is(getFooInnerScope.expanded, true,
"|person.getFoo| inner scope should be expanded at this point.");
is(getNameInnerScope.expanded, true,
"|person.getName| inner scope should be expanded at this point.");
// Only test that each function closes over the necessary variable.
// We wouldn't want future SpiderMonkey closure space
// optimizations to break this test.
is(getFooInnerScope.get("foo").target.querySelector(".name")
.getAttribute("value"), "foo",
"Found the foo node for the getFoo inner scope.");
is(getFooInnerScope.get("foo").target.querySelector(".value")
.getAttribute("value"), "10",
"The foo node has the expected value.");
is(getNameInnerScope.get("name").target.querySelector(".name")
.getAttribute("value"), "name",
"Found the name node for the getName inner scope.");
is(getNameInnerScope.get("name").target.querySelector(".value")
.getAttribute("value"), '"Bob"',
"The name node has the expected value.");
});
});
});
});
});
}
}
|