Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
3 "use strict";
5 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
7 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
9 // Disable logging for faster test runs. Set this pref to true if you want to
10 // debug a test in your try runs. Both the debugger server and frontend will
11 // be affected by this pref.
12 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
13 Services.prefs.setBoolPref("devtools.debugger.log", false);
15 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
16 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
17 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
18 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
19 let { require } = devtools;
20 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
21 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
22 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
23 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
24 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
25 const { promiseInvoke } = require("devtools/async-utils");
26 let TargetFactory = devtools.TargetFactory;
27 let Toolbox = devtools.Toolbox;
29 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
31 gDevTools.testing = true;
32 SimpleTest.registerCleanupFunction(() => {
33 gDevTools.testing = false;
34 });
36 // All tests are asynchronous.
37 waitForExplicitFinish();
39 registerCleanupFunction(function() {
40 info("finish() was called, cleaning up...");
41 Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
43 // Properly shut down the server to avoid memory leaks.
44 DebuggerServer.destroy();
46 // Debugger tests use a lot of memory, so force a GC to help fragmentation.
47 info("Forcing GC after debugger test.");
48 Cu.forceGC();
49 });
51 // Import the GCLI test helper
52 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
53 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
55 // Redeclare dbg_assert with a fatal behavior.
56 function dbg_assert(cond, e) {
57 if (!cond) {
58 throw e;
59 }
60 }
62 function addWindow(aUrl) {
63 info("Adding window: " + aUrl);
64 return promise.resolve(getDOMWindow(window.open(aUrl)));
65 }
67 function getDOMWindow(aReference) {
68 return aReference
69 .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
70 .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
71 .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
72 }
74 function addTab(aUrl, aWindow) {
75 info("Adding tab: " + aUrl);
77 let deferred = promise.defer();
78 let targetWindow = aWindow || window;
79 let targetBrowser = targetWindow.gBrowser;
81 targetWindow.focus();
82 let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
83 let linkedBrowser = tab.linkedBrowser;
85 linkedBrowser.addEventListener("load", function onLoad() {
86 linkedBrowser.removeEventListener("load", onLoad, true);
87 info("Tab added and finished loading: " + aUrl);
88 deferred.resolve(tab);
89 }, true);
91 return deferred.promise;
92 }
94 function removeTab(aTab, aWindow) {
95 info("Removing tab.");
97 let deferred = promise.defer();
98 let targetWindow = aWindow || window;
99 let targetBrowser = targetWindow.gBrowser;
100 let tabContainer = targetBrowser.tabContainer;
102 tabContainer.addEventListener("TabClose", function onClose(aEvent) {
103 tabContainer.removeEventListener("TabClose", onClose, false);
104 info("Tab removed and finished closing.");
105 deferred.resolve();
106 }, false);
108 targetBrowser.removeTab(aTab);
109 return deferred.promise;
110 }
112 function addAddon(aUrl) {
113 info("Installing addon: " + aUrl);
115 let deferred = promise.defer();
117 AddonManager.getInstallForURL(aUrl, aInstaller => {
118 aInstaller.install();
119 let listener = {
120 onInstallEnded: function(aAddon, aAddonInstall) {
121 aInstaller.removeListener(listener);
123 // Wait for add-on's startup scripts to execute. See bug 997408
124 executeSoon(function() {
125 deferred.resolve(aAddonInstall);
126 });
127 }
128 };
129 aInstaller.addListener(listener);
130 }, "application/x-xpinstall");
132 return deferred.promise;
133 }
135 function removeAddon(aAddon) {
136 info("Removing addon.");
138 let deferred = promise.defer();
140 let listener = {
141 onUninstalled: function(aUninstalledAddon) {
142 if (aUninstalledAddon != aAddon) {
143 return;
144 }
145 AddonManager.removeAddonListener(listener);
146 deferred.resolve();
147 }
148 };
149 AddonManager.addAddonListener(listener);
150 aAddon.uninstall();
152 return deferred.promise;
153 }
155 function getTabActorForUrl(aClient, aUrl) {
156 let deferred = promise.defer();
158 aClient.listTabs(aResponse => {
159 let tabActor = aResponse.tabs.filter(aGrip => aGrip.url == aUrl).pop();
160 deferred.resolve(tabActor);
161 });
163 return deferred.promise;
164 }
166 function getAddonActorForUrl(aClient, aUrl) {
167 info("Get addon actor for URL: " + aUrl);
168 let deferred = promise.defer();
170 aClient.listAddons(aResponse => {
171 let addonActor = aResponse.addons.filter(aGrip => aGrip.url == aUrl).pop();
172 info("got addon actor for URL: " + addonActor.actor);
173 deferred.resolve(addonActor);
174 });
176 return deferred.promise;
177 }
179 function attachTabActorForUrl(aClient, aUrl) {
180 let deferred = promise.defer();
182 getTabActorForUrl(aClient, aUrl).then(aGrip => {
183 aClient.attachTab(aGrip.actor, aResponse => {
184 deferred.resolve([aGrip, aResponse]);
185 });
186 });
188 return deferred.promise;
189 }
191 function attachThreadActorForUrl(aClient, aUrl) {
192 let deferred = promise.defer();
194 attachTabActorForUrl(aClient, aUrl).then(([aGrip, aResponse]) => {
195 aClient.attachThread(aResponse.threadActor, (aResponse, aThreadClient) => {
196 aThreadClient.resume(aResponse => {
197 deferred.resolve(aThreadClient);
198 });
199 });
200 });
202 return deferred.promise;
203 }
205 function once(aTarget, aEventName, aUseCapture = false) {
206 info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
208 let deferred = promise.defer();
210 for (let [add, remove] of [
211 ["addEventListener", "removeEventListener"],
212 ["addListener", "removeListener"],
213 ["on", "off"]
214 ]) {
215 if ((add in aTarget) && (remove in aTarget)) {
216 aTarget[add](aEventName, function onEvent(...aArgs) {
217 aTarget[remove](aEventName, onEvent, aUseCapture);
218 deferred.resolve.apply(deferred, aArgs);
219 }, aUseCapture);
220 break;
221 }
222 }
224 return deferred.promise;
225 }
227 function waitForTick() {
228 let deferred = promise.defer();
229 executeSoon(deferred.resolve);
230 return deferred.promise;
231 }
233 function waitForTime(aDelay) {
234 let deferred = promise.defer();
235 setTimeout(deferred.resolve, aDelay);
236 return deferred.promise;
237 }
239 function waitForSourceShown(aPanel, aUrl) {
240 return waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.SOURCE_SHOWN).then(aSource => {
241 let sourceUrl = aSource.url;
242 info("Source shown: " + sourceUrl);
244 if (!sourceUrl.contains(aUrl)) {
245 return waitForSourceShown(aPanel, aUrl);
246 } else {
247 ok(true, "The correct source has been shown.");
248 }
249 });
250 }
252 function waitForEditorLocationSet(aPanel) {
253 return waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.EDITOR_LOCATION_SET);
254 }
256 function ensureSourceIs(aPanel, aUrl, aWaitFlag = false) {
257 if (aPanel.panelWin.DebuggerView.Sources.selectedValue.contains(aUrl)) {
258 ok(true, "Expected source is shown: " + aUrl);
259 return promise.resolve(null);
260 }
261 if (aWaitFlag) {
262 return waitForSourceShown(aPanel, aUrl);
263 }
264 ok(false, "Expected source was not already shown: " + aUrl);
265 return promise.reject(null);
266 }
268 function waitForCaretUpdated(aPanel, aLine, aCol = 1) {
269 return waitForEditorEvents(aPanel, "cursorActivity").then(() => {
270 let cursor = aPanel.panelWin.DebuggerView.editor.getCursor();
271 info("Caret updated: " + (cursor.line + 1) + ", " + (cursor.ch + 1));
273 if (!isCaretPos(aPanel, aLine, aCol)) {
274 return waitForCaretUpdated(aPanel, aLine, aCol);
275 } else {
276 ok(true, "The correct caret position has been set.");
277 }
278 });
279 }
281 function ensureCaretAt(aPanel, aLine, aCol = 1, aWaitFlag = false) {
282 if (isCaretPos(aPanel, aLine, aCol)) {
283 ok(true, "Expected caret position is set: " + aLine + "," + aCol);
284 return promise.resolve(null);
285 }
286 if (aWaitFlag) {
287 return waitForCaretUpdated(aPanel, aLine, aCol);
288 }
289 ok(false, "Expected caret position was not already set: " + aLine + "," + aCol);
290 return promise.reject(null);
291 }
293 function isCaretPos(aPanel, aLine, aCol = 1) {
294 let editor = aPanel.panelWin.DebuggerView.editor;
295 let cursor = editor.getCursor();
297 // Source editor starts counting line and column numbers from 0.
298 info("Current editor caret position: " + (cursor.line + 1) + ", " + (cursor.ch + 1));
299 return cursor.line == (aLine - 1) && cursor.ch == (aCol - 1);
300 }
302 function isEditorSel(aPanel, [start, end]) {
303 let editor = aPanel.panelWin.DebuggerView.editor;
304 let range = {
305 start: editor.getOffset(editor.getCursor("start")),
306 end: editor.getOffset(editor.getCursor())
307 };
309 // Source editor starts counting line and column numbers from 0.
310 info("Current editor selection: " + (range.start + 1) + ", " + (range.end + 1));
311 return range.start == (start - 1) && range.end == (end - 1);
312 }
314 function waitForSourceAndCaret(aPanel, aUrl, aLine, aCol) {
315 return promise.all([
316 waitForSourceShown(aPanel, aUrl),
317 waitForCaretUpdated(aPanel, aLine, aCol)
318 ]);
319 }
321 function waitForCaretAndScopes(aPanel, aLine, aCol) {
322 return promise.all([
323 waitForCaretUpdated(aPanel, aLine, aCol),
324 waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.FETCHED_SCOPES)
325 ]);
326 }
328 function waitForSourceAndCaretAndScopes(aPanel, aUrl, aLine, aCol) {
329 return promise.all([
330 waitForSourceAndCaret(aPanel, aUrl, aLine, aCol),
331 waitForDebuggerEvents(aPanel, aPanel.panelWin.EVENTS.FETCHED_SCOPES)
332 ]);
333 }
335 function waitForDebuggerEvents(aPanel, aEventName, aEventRepeat = 1) {
336 info("Waiting for debugger event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
338 let deferred = promise.defer();
339 let panelWin = aPanel.panelWin;
340 let count = 0;
342 panelWin.on(aEventName, function onEvent(aEventName, ...aArgs) {
343 info("Debugger event '" + aEventName + "' fired: " + (++count) + " time(s).");
345 if (count == aEventRepeat) {
346 ok(true, "Enough '" + aEventName + "' panel events have been fired.");
347 panelWin.off(aEventName, onEvent);
348 deferred.resolve.apply(deferred, aArgs);
349 }
350 });
352 return deferred.promise;
353 }
355 function waitForEditorEvents(aPanel, aEventName, aEventRepeat = 1) {
356 info("Waiting for editor event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
358 let deferred = promise.defer();
359 let editor = aPanel.panelWin.DebuggerView.editor;
360 let count = 0;
362 editor.on(aEventName, function onEvent(...aArgs) {
363 info("Editor event '" + aEventName + "' fired: " + (++count) + " time(s).");
365 if (count == aEventRepeat) {
366 ok(true, "Enough '" + aEventName + "' editor events have been fired.");
367 editor.off(aEventName, onEvent);
368 deferred.resolve.apply(deferred, aArgs);
369 }
370 });
372 return deferred.promise;
373 }
375 function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) {
376 info("Waiting for thread event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
378 let deferred = promise.defer();
379 let thread = aPanel.panelWin.gThreadClient;
380 let count = 0;
382 thread.addListener(aEventName, function onEvent(aEventName, ...aArgs) {
383 info("Thread event '" + aEventName + "' fired: " + (++count) + " time(s).");
385 if (count == aEventRepeat) {
386 ok(true, "Enough '" + aEventName + "' thread events have been fired.");
387 thread.removeListener(aEventName, onEvent);
388 deferred.resolve.apply(deferred, aArgs);
389 }
390 });
392 return deferred.promise;
393 }
395 function waitForClientEvents(aPanel, aEventName, aEventRepeat = 1) {
396 info("Waiting for client event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
398 let deferred = promise.defer();
399 let client = aPanel.panelWin.gClient;
400 let count = 0;
402 client.addListener(aEventName, function onEvent(aEventName, ...aArgs) {
403 info("Thread event '" + aEventName + "' fired: " + (++count) + " time(s).");
405 if (count == aEventRepeat) {
406 ok(true, "Enough '" + aEventName + "' thread events have been fired.");
407 client.removeListener(aEventName, onEvent);
408 deferred.resolve.apply(deferred, aArgs);
409 }
410 });
412 return deferred.promise;
413 }
415 function ensureThreadClientState(aPanel, aState) {
416 let thread = aPanel.panelWin.gThreadClient;
417 let state = thread.state;
419 info("Thread is: '" + state + "'.");
421 if (state == aState) {
422 return promise.resolve(null);
423 } else {
424 return waitForThreadEvents(aPanel, aState);
425 }
426 }
428 function navigateActiveTabTo(aPanel, aUrl, aWaitForEventName, aEventRepeat) {
429 let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat);
430 let activeTab = aPanel.panelWin.DebuggerController._target.activeTab;
431 aUrl ? activeTab.navigateTo(aUrl) : activeTab.reload();
432 return finished;
433 }
435 function navigateActiveTabInHistory(aPanel, aDirection, aWaitForEventName, aEventRepeat) {
436 let finished = waitForDebuggerEvents(aPanel, aWaitForEventName, aEventRepeat);
437 content.history[aDirection]();
438 return finished;
439 }
441 function reloadActiveTab(aPanel, aWaitForEventName, aEventRepeat) {
442 return navigateActiveTabTo(aPanel, null, aWaitForEventName, aEventRepeat);
443 }
445 function clearText(aElement) {
446 info("Clearing text...");
447 aElement.focus();
448 aElement.value = "";
449 }
451 function setText(aElement, aText) {
452 clearText(aElement);
453 info("Setting text: " + aText);
454 aElement.value = aText;
455 }
457 function typeText(aElement, aText) {
458 info("Typing text: " + aText);
459 aElement.focus();
460 EventUtils.sendString(aText, aElement.ownerDocument.defaultView);
461 }
463 function backspaceText(aElement, aTimes) {
464 info("Pressing backspace " + aTimes + " times.");
465 for (let i = 0; i < aTimes; i++) {
466 aElement.focus();
467 EventUtils.sendKey("BACK_SPACE", aElement.ownerDocument.defaultView);
468 }
469 }
471 function getTab(aTarget, aWindow) {
472 if (aTarget instanceof XULElement) {
473 return promise.resolve(aTarget);
474 } else {
475 return addTab(aTarget, aWindow);
476 }
477 }
479 function getSources(aClient) {
480 let deferred = promise.defer();
482 aClient.getSources(({sources}) => deferred.resolve(sources));
484 return deferred.promise;
485 }
487 function initDebugger(aTarget, aWindow) {
488 info("Initializing a debugger panel.");
490 return getTab(aTarget, aWindow).then(aTab => {
491 info("Debugee tab added successfully: " + aTarget);
493 let deferred = promise.defer();
494 let debuggee = aTab.linkedBrowser.contentWindow.wrappedJSObject;
495 let target = TargetFactory.forTab(aTab);
497 gDevTools.showToolbox(target, "jsdebugger").then(aToolbox => {
498 info("Debugger panel shown successfully.");
500 let debuggerPanel = aToolbox.getCurrentPanel();
501 let panelWin = debuggerPanel.panelWin;
503 // Wait for the initial resume...
504 panelWin.gClient.addOneTimeListener("resumed", () => {
505 info("Debugger client resumed successfully.");
507 prepareDebugger(debuggerPanel);
508 deferred.resolve([aTab, debuggee, debuggerPanel, aWindow]);
509 });
510 });
512 return deferred.promise;
513 });
514 }
516 // Creates an add-on debugger for a given add-on. The returned AddonDebugger
517 // object must be destroyed before finishing the test
518 function initAddonDebugger(aUrl) {
519 let addonDebugger = new AddonDebugger();
520 return addonDebugger.init(aUrl).then(() => addonDebugger);
521 }
523 function AddonDebugger() {
524 this._onMessage = this._onMessage.bind(this);
525 }
527 AddonDebugger.prototype = {
528 init: Task.async(function*(aUrl) {
529 info("Initializing an addon debugger panel.");
531 if (!DebuggerServer.initialized) {
532 DebuggerServer.init(() => true);
533 DebuggerServer.addBrowserActors();
534 }
536 this.frame = document.createElement("iframe");
537 this.frame.setAttribute("height", 400);
538 document.documentElement.appendChild(this.frame);
539 window.addEventListener("message", this._onMessage);
541 let transport = DebuggerServer.connectPipe();
542 this.client = new DebuggerClient(transport);
544 let connected = promise.defer();
545 this.client.connect(connected.resolve);
546 yield connected.promise;
548 let addonActor = yield getAddonActorForUrl(this.client, aUrl);
550 let targetOptions = {
551 form: { addonActor: addonActor.actor, title: addonActor.name },
552 client: this.client,
553 chrome: true
554 };
556 let toolboxOptions = {
557 customIframe: this.frame
558 };
560 let target = devtools.TargetFactory.forTab(targetOptions);
561 let toolbox = yield gDevTools.showToolbox(target, "jsdebugger", devtools.Toolbox.HostType.CUSTOM, toolboxOptions);
563 info("Addon debugger panel shown successfully.");
565 this.debuggerPanel = toolbox.getCurrentPanel();
567 // Wait for the initial resume...
568 yield waitForClientEvents(this.debuggerPanel, "resumed");
569 yield prepareDebugger(this.debuggerPanel);
570 }),
572 destroy: Task.async(function*() {
573 let deferred = promise.defer();
574 this.client.close(deferred.resolve);
575 yield deferred.promise;
576 yield this.debuggerPanel._toolbox.destroy();
577 this.frame.remove();
578 window.removeEventListener("message", this._onMessage);
579 }),
581 /**
582 * Returns a list of the groups and sources in the UI. The returned array
583 * contains objects for each group with properties name and sources. The
584 * sources property contains an array with objects for each source for that
585 * group with properties label and url.
586 */
587 getSourceGroups: Task.async(function*() {
588 let debuggerWin = this.debuggerPanel.panelWin;
589 let sources = yield getSources(debuggerWin.gThreadClient);
590 ok(sources.length, "retrieved sources");
592 // groups will be the return value, groupmap and the maps we put in it will
593 // be used as quick lookups to add the url information in below
594 let groups = [];
595 let groupmap = new Map();
597 let uigroups = this.debuggerPanel.panelWin.document.querySelectorAll(".side-menu-widget-group");
598 for (let g of uigroups) {
599 let name = g.querySelector(".side-menu-widget-group-title .name").value;
600 let group = {
601 name: name,
602 sources: []
603 };
604 groups.push(group);
605 let labelmap = new Map();
606 groupmap.set(name, labelmap);
608 for (let l of g.querySelectorAll(".dbg-source-item")) {
609 let source = {
610 label: l.value,
611 url: null
612 };
614 labelmap.set(l.value, source);
615 group.sources.push(source);
616 }
617 }
619 for (let source of sources) {
620 let { label, group } = debuggerWin.DebuggerView.Sources.getItemByValue(source.url).attachment;
622 if (!groupmap.has(group)) {
623 ok(false, "Saw a source group not in the UI: " + group);
624 continue;
625 }
627 if (!groupmap.get(group).has(label)) {
628 ok(false, "Saw a source label not in the UI: " + label);
629 continue;
630 }
632 groupmap.get(group).get(label).url = source.url.split(" -> ").pop();
633 }
635 return groups;
636 }),
638 _onMessage: function(event) {
639 let json = JSON.parse(event.data);
640 switch (json.name) {
641 case "toolbox-title":
642 this.title = json.data.value;
643 break;
644 }
645 }
646 }
648 function initChromeDebugger(aOnClose) {
649 info("Initializing a chrome debugger process.");
651 let deferred = promise.defer();
653 // Wait for the toolbox process to start...
654 BrowserToolboxProcess.init(aOnClose, (aEvent, aProcess) => {
655 info("Browser toolbox process started successfully.");
657 prepareDebugger(aProcess);
658 deferred.resolve(aProcess);
659 });
661 return deferred.promise;
662 }
664 function prepareDebugger(aDebugger) {
665 if ("target" in aDebugger) {
666 let view = aDebugger.panelWin.DebuggerView;
667 view.Variables.lazyEmpty = false;
668 view.Variables.lazySearch = false;
669 view.FilteredSources._autoSelectFirstItem = true;
670 view.FilteredFunctions._autoSelectFirstItem = true;
671 } else {
672 // Nothing to do here yet.
673 }
674 }
676 function teardown(aPanel, aFlags = {}) {
677 info("Destroying the specified debugger.");
679 let toolbox = aPanel._toolbox;
680 let tab = aPanel.target.tab;
681 let debuggerRootActorDisconnected = once(window, "Debugger:Shutdown");
682 let debuggerPanelDestroyed = once(aPanel, "destroyed");
683 let devtoolsToolboxDestroyed = toolbox.destroy();
685 return promise.all([
686 debuggerRootActorDisconnected,
687 debuggerPanelDestroyed,
688 devtoolsToolboxDestroyed
689 ]).then(() => aFlags.noTabRemoval ? null : removeTab(tab));
690 }
692 function closeDebuggerAndFinish(aPanel, aFlags = {}) {
693 let thread = aPanel.panelWin.gThreadClient;
694 if (thread.state == "paused" && !aFlags.whilePaused) {
695 ok(false, "You should use 'resumeDebuggerThenCloseAndFinish' instead, " +
696 "unless you're absolutely sure about what you're doing.");
697 }
698 return teardown(aPanel, aFlags).then(finish);
699 }
701 function resumeDebuggerThenCloseAndFinish(aPanel, aFlags = {}) {
702 let deferred = promise.defer();
703 let thread = aPanel.panelWin.gThreadClient;
704 thread.resume(() => closeDebuggerAndFinish(aPanel, aFlags).then(deferred.resolve));
705 return deferred.promise;
706 }
708 // Blackboxing helpers
710 function getBlackBoxButton(aPanel) {
711 return aPanel.panelWin.document.getElementById("black-box");
712 }
714 function toggleBlackBoxing(aPanel, aSource = null) {
715 function clickBlackBoxButton() {
716 getBlackBoxButton(aPanel).click();
717 }
719 const blackBoxChanged = waitForThreadEvents(aPanel, "blackboxchange");
721 if (aSource) {
722 aPanel.panelWin.DebuggerView.Sources.selectedValue = aSource;
723 ensureSourceIs(aPanel, aSource, true).then(clickBlackBoxButton);
724 } else {
725 clickBlackBoxButton();
726 }
728 return blackBoxChanged;
729 }
731 function selectSourceAndGetBlackBoxButton(aPanel, aSource) {
732 function returnBlackboxButton() {
733 return getBlackBoxButton(aPanel);
734 }
736 aPanel.panelWin.DebuggerView.Sources.selectedValue = aSource;
737 return ensureSourceIs(aPanel, aSource, true).then(returnBlackboxButton);
738 }
740 // Variables view inspection popup helpers
742 function openVarPopup(aPanel, aCoords, aWaitForFetchedProperties) {
743 let events = aPanel.panelWin.EVENTS;
744 let editor = aPanel.panelWin.DebuggerView.editor;
745 let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
746 let tooltip = bubble._tooltip.panel;
748 let popupShown = once(tooltip, "popupshown");
749 let fetchedProperties = aWaitForFetchedProperties
750 ? waitForDebuggerEvents(aPanel, events.FETCHED_BUBBLE_PROPERTIES)
751 : promise.resolve(null);
753 let { left, top } = editor.getCoordsFromPosition(aCoords);
754 bubble._findIdentifier(left, top);
755 return promise.all([popupShown, fetchedProperties]).then(waitForTick);
756 }
758 // Simulates the mouse hovering a variable in the debugger
759 // Takes in account the position of the cursor in the text, if the text is
760 // selected and if a button is currently pushed (aButtonPushed > 0).
761 // The function returns a promise which returns true if the popup opened or
762 // false if it didn't
763 function intendOpenVarPopup(aPanel, aPosition, aButtonPushed) {
764 let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
765 let editor = aPanel.panelWin.DebuggerView.editor;
766 let tooltip = bubble._tooltip;
768 let { left, top } = editor.getCoordsFromPosition(aPosition);
770 const eventDescriptor = {
771 clientX: left,
772 clientY: top,
773 buttons: aButtonPushed
774 };
776 bubble._onMouseMove(eventDescriptor);
778 const deferred = promise.defer();
779 window.setTimeout(
780 function() {
781 if(tooltip.isEmpty()) {
782 deferred.resolve(false);
783 } else {
784 deferred.resolve(true);
785 }
786 },
787 tooltip.defaultShowDelay + 1000
788 );
790 return deferred.promise;
791 }
793 function hideVarPopup(aPanel) {
794 let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
795 let tooltip = bubble._tooltip.panel;
797 let popupHiding = once(tooltip, "popuphiding");
798 bubble.hideContents();
799 return popupHiding.then(waitForTick);
800 }
802 function hideVarPopupByScrollingEditor(aPanel) {
803 let editor = aPanel.panelWin.DebuggerView.editor;
804 let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
805 let tooltip = bubble._tooltip.panel;
807 let popupHiding = once(tooltip, "popuphiding");
808 editor.setFirstVisibleLine(0);
809 return popupHiding.then(waitForTick);
810 }
812 function reopenVarPopup(...aArgs) {
813 return hideVarPopup.apply(this, aArgs).then(() => openVarPopup.apply(this, aArgs));
814 }
816 // Tracing helpers
818 function startTracing(aPanel) {
819 const deferred = promise.defer();
820 aPanel.panelWin.DebuggerController.Tracer.startTracing(aResponse => {
821 if (aResponse.error) {
822 deferred.reject(aResponse);
823 } else {
824 deferred.resolve(aResponse);
825 }
826 });
827 return deferred.promise;
828 }
830 function stopTracing(aPanel) {
831 const deferred = promise.defer();
832 aPanel.panelWin.DebuggerController.Tracer.stopTracing(aResponse => {
833 if (aResponse.error) {
834 deferred.reject(aResponse);
835 } else {
836 deferred.resolve(aResponse);
837 }
838 });
839 return deferred.promise;
840 }
842 function filterTraces(aPanel, f) {
843 const traces = aPanel.panelWin.document
844 .getElementById("tracer-traces")
845 .querySelector("scrollbox")
846 .children;
847 return Array.filter(traces, f);
848 }
849 function attachAddonActorForUrl(aClient, aUrl) {
850 let deferred = promise.defer();
852 getAddonActorForUrl(aClient, aUrl).then(aGrip => {
853 aClient.attachAddon(aGrip.actor, aResponse => {
854 deferred.resolve([aGrip, aResponse]);
855 });
856 });
858 return deferred.promise;
859 }
861 function rdpInvoke(aClient, aMethod, ...args) {
862 return promiseInvoke(aClient, aMethod, ...args)
863 .then(({error, message }) => {
864 if (error) {
865 throw new Error(error + ": " + message);
866 }
867 });
868 }
870 function doResume(aPanel) {
871 const threadClient = aPanel.panelWin.gThreadClient;
872 return rdpInvoke(threadClient, threadClient.resume);
873 }
875 function doInterrupt(aPanel) {
876 const threadClient = aPanel.panelWin.gThreadClient;
877 return rdpInvoke(threadClient, threadClient.interrupt);
878 }