/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Bug 723069: test the debugger breakpoint API and connection to the source * editor. */ const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html"; let gPane = null; let gTab = null; let gDebuggee = null; let gDebugger = null; let gEditor = null; let gSources = null; let gBreakpoints = null; function test() { let scriptShown = false; let framesAdded = false; let resumed = false; let testStarted = false; debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.panelWin; resumed = true; gDebugger.addEventListener("Debugger:SourceShown", onSourceShown); gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() { framesAdded = true; executeSoon(startTest); }); executeSoon(function() { gDebuggee.firstCall(); }); }); function onSourceShown(aEvent) { scriptShown = aEvent.detail.url.indexOf("-02.js") != -1; executeSoon(startTest); } function startTest() { if (scriptShown && framesAdded && !testStarted) { gDebugger.removeEventListener("Debugger:SourceShown", onSourceShown); testStarted = true; Services.tm.currentThread.dispatch({ run: performTest }, 0); } } function performTest() { gSources = gDebugger.DebuggerView.Sources; gEditor = gDebugger.editor; gBreakpoints = gPane.getAllBreakpoints(); is(gDebugger.DebuggerController.activeThread.state, "paused", "Should only be getting stack frames while paused."); is(gSources.itemCount, 2, "Found the expected number of scripts."); isnot(gEditor.getText().indexOf("debugger"), -1, "The correct script was loaded initially."); isnot(gSources.selectedValue, gSources.values[0], "The correct script is selected"); is(Object.keys(gBreakpoints), 0, "no breakpoints"); ok(!gPane.getBreakpoint("foo", 3), "getBreakpoint('foo', 3) returns falsey"); is(gEditor.getBreakpoints().length, 0, "no breakpoints in the editor"); gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst); executeSoon(function() { gPane.addBreakpoint({url: gSources.selectedValue, line: 6}, onBreakpointAddFirst); }); } let breakpointsAdded = 0; let breakpointsRemoved = 0; let editorBreakpointChanges = 0; function onEditorBreakpointAddFirst(aEvent) { gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst); editorBreakpointChanges++; ok(aEvent, "breakpoint1 added to the editor"); is(aEvent.added.length, 1, "one breakpoint added to the editor"); is(aEvent.removed.length, 0, "no breakpoint was removed from the editor"); is(aEvent.added[0].line, 5, "editor breakpoint line is correct"); is(gEditor.getBreakpoints().length, 1, "editor.getBreakpoints().length is correct"); } function onBreakpointAddFirst(aBreakpointClient, aResponseError) { breakpointsAdded++; ok(aBreakpointClient, "breakpoint1 added, client received"); ok(!aResponseError, "breakpoint1 added without errors"); is(aBreakpointClient.location.url, gSources.selectedValue, "breakpoint1 client url is correct"); is(aBreakpointClient.location.line, 6, "breakpoint1 client line is correct"); executeSoon(function() { ok(aBreakpointClient.actor in gBreakpoints, "breakpoint1 client found in the list of debugger breakpoints"); is(Object.keys(gBreakpoints).length, 1, "the list of debugger breakpoints holds only one breakpoint"); is(gPane.getBreakpoint(gSources.selectedValue, 6), aBreakpointClient, "getBreakpoint returns the correct breakpoint"); info("remove the first breakpoint"); gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst); gPane.removeBreakpoint(aBreakpointClient, onBreakpointRemoveFirst); }); } function onBreakpointRemoveFirst(aLocation) { breakpointsRemoved++; ok(aLocation, "breakpoint1 removed"); is(aLocation.url, gSources.selectedValue, "breakpoint1 remove: url is correct"); is(aLocation.line, 6, "breakpoint1 remove: line is correct"); executeSoon(testBreakpointAddBackground); } function onEditorBreakpointRemoveFirst(aEvent) { gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst); editorBreakpointChanges++; ok(aEvent, "breakpoint1 removed from the editor"); is(aEvent.added.length, 0, "no breakpoint was added to the editor"); is(aEvent.removed.length, 1, "one breakpoint was removed from the editor"); is(aEvent.removed[0].line, 5, "editor breakpoint line is correct"); is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct"); } function testBreakpointAddBackground() { info("add a breakpoint to the second script which is not selected"); is(Object.keys(gBreakpoints).length, 0, "no breakpoints in the debugger"); ok(!gPane.getBreakpoint(gSources.selectedValue, 6), "getBreakpoint(selectedScript, 6) returns no breakpoint"); isnot(gSources.values[0], gSources.selectedValue, "first script location is not the currently selected script"); gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); gPane.addBreakpoint({url: gSources.values[0], line: 5}, onBreakpointAddBackground); } function onEditorBreakpointAddBackgroundTrap(aEvent) { // Trap listener: no breakpoint must be added to the editor when a // breakpoint is added to a script that is not currently selected. gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); editorBreakpointChanges++; ok(false, "breakpoint2 must not be added to the editor"); } function onBreakpointAddBackground(aBreakpointClient, aResponseError) { breakpointsAdded++; ok(aBreakpointClient, "breakpoint2 added, client received"); ok(!aResponseError, "breakpoint2 added without errors"); is(aBreakpointClient.location.url, gSources.values[0], "breakpoint2 client url is correct"); is(aBreakpointClient.location.line, 5, "breakpoint2 client line is correct"); executeSoon(function() { ok(aBreakpointClient.actor in gBreakpoints, "breakpoint2 client found in the list of debugger breakpoints"); is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger"); is(gPane.getBreakpoint(gSources.values[0], 5), aBreakpointClient, "getBreakpoint(locations[0], 5) returns the correct breakpoint"); // remove the trap listener gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); info("switch to the second script"); gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch); gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged); gSources.selectedIndex = 0; }); } function onEditorBreakpointAddSwitch(aEvent) { gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch); editorBreakpointChanges++; ok(aEvent, "breakpoint2 added to the editor"); is(aEvent.added.length, 1, "one breakpoint added to the editor"); is(aEvent.removed.length, 0, "no breakpoint was removed from the editor"); is(aEvent.added[0].line, 4, "editor breakpoint line is correct"); is(gEditor.getBreakpoints().length, 1, "editor.getBreakpoints().length is correct"); } function onEditorTextChanged() { // Wait for the actual text to be shown. if (gDebugger.editor.getText() == gDebugger.L10N.getStr("loadingText")) { return; } // The requested source text has been shown, remove the event listener. gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged); is(gEditor.getText().indexOf("debugger"), -1, "The second script is no longer displayed."); isnot(gEditor.getText().indexOf("firstCall"), -1, "The first script is displayed."); let window = gEditor.editorElement.contentWindow; executeSoon(() => window.mozRequestAnimationFrame(onReadyForClick)); } function onReadyForClick() { info("remove the second breakpoint using the mouse"); gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond); let iframe = gEditor.editorElement; let testWin = iframe.ownerDocument.defaultView; // flush the layout for the iframe info("rect " + iframe.contentDocument.documentElement.getBoundingClientRect()); let utils = testWin.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let lineOffset = gEditor.getLineStart(4); let coords = gEditor.getLocationAtOffset(lineOffset); let rect = iframe.getBoundingClientRect(); let left = rect.left + 10; let top = rect.top + coords.y + 4; utils.sendMouseEventToWindow("mousedown", left, top, 0, 1, 0, false, 0, 0); utils.sendMouseEventToWindow("mouseup", left, top, 0, 1, 0, false, 0, 0); } function onEditorBreakpointRemoveSecond(aEvent) { gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond); editorBreakpointChanges++; ok(aEvent, "breakpoint2 removed from the editor"); is(aEvent.added.length, 0, "no breakpoint was added to the editor"); is(aEvent.removed.length, 1, "one breakpoint was removed from the editor"); is(aEvent.removed[0].line, 4, "editor breakpoint line is correct"); is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct"); executeSoon(function() { gDebugger.gClient.addOneTimeListener("resumed", function() { finalCheck(); closeDebuggerAndFinish(); }); gDebugger.DebuggerController.activeThread.resume(); }); } function finalCheck() { is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger"); ok(!gPane.getBreakpoint(gSources.values[0], 5), "getBreakpoint(locations[0], 5) returns no breakpoint"); } registerCleanupFunction(function() { removeTab(gTab); is(breakpointsAdded, 2, "correct number of breakpoints have been added"); is(breakpointsRemoved, 1, "correct number of breakpoints have been removed"); is(editorBreakpointChanges, 4, "correct number of editor breakpoint changes"); gPane = null; gTab = null; gDebuggee = null; gDebugger = null; gEditor = null; gSources = null; gBreakpoints = null; }); }