browser/devtools/debugger/test/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial