gecko/browser/devtools/debugger/test/browser_dbg_break-on-dom-event.js
Panos Astithas 71a3267914 Make the debugger frontend cope with an already connected target (bug 933212); r=jryans,fitzgen
* Made the DebuggerClient, which is actually the RootActor front, not consider one of the attached child fronts as "active". Since a single DebuggerClient (or RootFront) is kept around for the App Manager's lifetime, it makes sense to move the notion of "active" tab to the toolbox's target. As each toolbox gets destroyed, the fronts should be detaching from their actors (if they are stateful) so that the app is no longer in a debugging state. Debugging a new app (or reconnecting to a previous one) will create new fronts anyway.
* Slightly refactored the TabClient, ThreadClient, SourceClient and TracerClient towards a protocol.js-based architecture, by adding parent-child references and lifecycle management. Now a tab-scoped thread actor for instance has the tab as its parent, while a global-scoped thread actor (chrome debugger) has the DebuggerCLient (RootFront) as its parent. This lets parents reference their children, so that caching in the target object can work. It also allowed me to move some methods from the DebuggerClient to the actual front that should be responsible, like reconfigureTab, reconfigureThread and attachThread. These methods now use DebuggerClient.requester, too.
* Added some error handling in the debugger client requester around "before" and "after" callbacks, which exposed some errors in tests that are now fixed.
* Fixed the state handling in the thread actor so that merely detaching from a thread doesn't put it in the exited state. This is the part that what was necessary for Firebug's use case.
* Properly loading tracer and webgl actors now on b2g.
2014-01-14 17:39:40 +02:00

231 lines
6.3 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the break-on-dom-events request works.
*/
const TAB_URL = EXAMPLE_URL + "doc_event-listeners.html";
let gClient, gThreadClient, gInput, gButton;
function test() {
if (!DebuggerServer.initialized) {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
gClient.connect((aType, aTraits) => {
is(aType, "browser",
"Root actor should identify itself as a browser.");
addTab(TAB_URL)
.then(() => attachThreadActorForUrl(gClient, TAB_URL))
.then(setupGlobals)
.then(pauseDebuggee)
.then(testBreakOnAll)
.then(testBreakOnDisabled)
.then(testBreakOnNone)
.then(testBreakOnClick)
.then(closeConnection)
.then(finish)
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
function setupGlobals(aThreadClient) {
gThreadClient = aThreadClient;
gInput = content.document.querySelector("input");
gButton = content.document.querySelector("button");
}
function pauseDebuggee() {
let deferred = promise.defer();
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.type, "paused",
"We should now be paused.");
is(aPacket.why.type, "debuggerStatement",
"The debugger statement was hit.");
deferred.resolve();
});
// Spin the event loop before causing the debuggee to pause, to allow
// this function to return first.
executeSoon(triggerButtonClick);
return deferred.promise;
}
// Test pause on all events.
function testBreakOnAll() {
let deferred = promise.defer();
// Test calling pauseOnDOMEvents from a paused state.
gThreadClient.pauseOnDOMEvents("*", (aPacket) => {
is(aPacket.error, undefined,
"The pause-on-any-event request completed successfully.");
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.why.type, "pauseOnDOMEvents",
"A hidden breakpoint was hit.");
is(aPacket.frame.callee.name, "keyupHandler",
"The keyupHandler is entered.");
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.why.type, "pauseOnDOMEvents",
"A hidden breakpoint was hit.");
is(aPacket.frame.callee.name, "clickHandler",
"The clickHandler is entered.");
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.why.type, "pauseOnDOMEvents",
"A hidden breakpoint was hit.");
is(aPacket.frame.callee.name, "onchange",
"The onchange handler is entered.");
gThreadClient.resume(deferred.resolve);
});
gThreadClient.resume(triggerInputChange);
});
gThreadClient.resume(triggerButtonClick);
});
gThreadClient.resume(triggerInputKeyup);
});
return deferred.promise;
}
// Test that removing events from the array disables them.
function testBreakOnDisabled() {
let deferred = promise.defer();
// Test calling pauseOnDOMEvents from a running state.
gThreadClient.pauseOnDOMEvents(["click"], (aPacket) => {
is(aPacket.error, undefined,
"The pause-on-click-only request completed successfully.");
gClient.addListener("paused", unexpectedListener);
// This non-capturing event listener is guaranteed to run after the page's
// capturing one had a chance to execute and modify window.foobar.
once(gInput, "keyup").then(() => {
is(content.wrappedJSObject.foobar, "keyupHandler",
"No hidden breakpoint was hit.");
gClient.removeListener("paused", unexpectedListener);
deferred.resolve();
});
triggerInputKeyup();
});
return deferred.promise;
}
// Test that specifying an empty event array clears all hidden breakpoints.
function testBreakOnNone() {
let deferred = promise.defer();
// Test calling pauseOnDOMEvents from a running state.
gThreadClient.pauseOnDOMEvents([], (aPacket) => {
is(aPacket.error, undefined,
"The pause-on-none request completed successfully.");
gClient.addListener("paused", unexpectedListener);
// This non-capturing event listener is guaranteed to run after the page's
// capturing one had a chance to execute and modify window.foobar.
once(gInput, "keyup").then(() => {
is(content.wrappedJSObject.foobar, "keyupHandler",
"No hidden breakpoint was hit.");
gClient.removeListener("paused", unexpectedListener);
deferred.resolve();
});
triggerInputKeyup();
});
return deferred.promise;
}
// Test pause on a single event.
function testBreakOnClick() {
let deferred = promise.defer();
// Test calling pauseOnDOMEvents from a running state.
gThreadClient.pauseOnDOMEvents(["click"], (aPacket) => {
is(aPacket.error, undefined,
"The pause-on-click request completed successfully.");
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.why.type, "pauseOnDOMEvents",
"A hidden breakpoint was hit.");
is(aPacket.frame.callee.name, "clickHandler",
"The clickHandler is entered.");
gThreadClient.resume(deferred.resolve);
});
triggerButtonClick();
});
return deferred.promise;
}
function closeConnection() {
let deferred = promise.defer();
gClient.close(deferred.resolve);
return deferred.promise;
}
function unexpectedListener() {
gClient.removeListener("paused", unexpectedListener);
ok(false, "An unexpected hidden breakpoint was hit.");
gThreadClient.resume(testBreakOnClick);
}
function triggerInputKeyup() {
// Make sure that the focus is not on the input box so that a focus event
// will be triggered.
window.focus();
gBrowser.selectedBrowser.focus();
gButton.focus();
// Focus the element and wait for focus event.
once(gInput, "focus").then(() => {
executeSoon(() => {
EventUtils.synthesizeKey("e", { shiftKey: 1 }, content);
});
});
gInput.focus();
}
function triggerButtonClick() {
EventUtils.sendMouseEvent({ type: "click" }, gButton);
}
function triggerInputChange() {
gInput.focus();
gInput.value = "foo";
gInput.blur();
}
registerCleanupFunction(function() {
removeTab(gBrowser.selectedTab);
gClient = null;
gThreadClient = null;
gInput = null;
gButton = null;
});