browser/devtools/webconsole/hudservice.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/webconsole/hudservice.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,738 @@
     1.4 +/* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
     1.5 +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +"use strict";
    1.11 +
    1.12 +const {Cc, Ci, Cu} = require("chrome");
    1.13 +
    1.14 +let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
    1.15 +let Heritage = require("sdk/core/heritage");
    1.16 +
    1.17 +loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry"));
    1.18 +loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame);
    1.19 +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
    1.20 +loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
    1.21 +loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm");
    1.22 +loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
    1.23 +loader.lazyImporter(this, "DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
    1.24 +loader.lazyImporter(this, "DebuggerClient", "resource://gre/modules/devtools/dbg-client.jsm");
    1.25 +
    1.26 +const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
    1.27 +let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
    1.28 +
    1.29 +const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
    1.30 +
    1.31 +// The preference prefix for all of the Browser Console filters.
    1.32 +const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
    1.33 +
    1.34 +let gHudId = 0;
    1.35 +
    1.36 +///////////////////////////////////////////////////////////////////////////
    1.37 +//// The HUD service
    1.38 +
    1.39 +function HUD_SERVICE()
    1.40 +{
    1.41 +  this.consoles = new Map();
    1.42 +  this.lastFinishedRequest = { callback: null };
    1.43 +}
    1.44 +
    1.45 +HUD_SERVICE.prototype =
    1.46 +{
    1.47 +  _browserConsoleID: null,
    1.48 +  _browserConsoleDefer: null,
    1.49 +
    1.50 +  /**
    1.51 +   * Keeps a reference for each Web Console / Browser Console that is created.
    1.52 +   * @type Map
    1.53 +   */
    1.54 +  consoles: null,
    1.55 +
    1.56 +  /**
    1.57 +   * Assign a function to this property to listen for every request that
    1.58 +   * completes. Used by unit tests. The callback takes one argument: the HTTP
    1.59 +   * activity object as received from the remote Web Console.
    1.60 +   *
    1.61 +   * @type object
    1.62 +   *       Includes a property named |callback|. Assign the function to the
    1.63 +   *       |callback| property of this object.
    1.64 +   */
    1.65 +  lastFinishedRequest: null,
    1.66 +
    1.67 +  /**
    1.68 +   * Firefox-specific current tab getter
    1.69 +   *
    1.70 +   * @returns nsIDOMWindow
    1.71 +   */
    1.72 +  currentContext: function HS_currentContext() {
    1.73 +    return Services.wm.getMostRecentWindow("navigator:browser");
    1.74 +  },
    1.75 +
    1.76 +  /**
    1.77 +   * Open a Web Console for the given target.
    1.78 +   *
    1.79 +   * @see devtools/framework/target.js for details about targets.
    1.80 +   *
    1.81 +   * @param object aTarget
    1.82 +   *        The target that the web console will connect to.
    1.83 +   * @param nsIDOMWindow aIframeWindow
    1.84 +   *        The window where the web console UI is already loaded.
    1.85 +   * @param nsIDOMWindow aChromeWindow
    1.86 +   *        The window of the web console owner.
    1.87 +   * @return object
    1.88 +   *         A promise object for the opening of the new WebConsole instance.
    1.89 +   */
    1.90 +  openWebConsole:
    1.91 +  function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
    1.92 +  {
    1.93 +    let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
    1.94 +    this.consoles.set(hud.hudId, hud);
    1.95 +    return hud.init();
    1.96 +  },
    1.97 +
    1.98 +  /**
    1.99 +   * Open a Browser Console for the given target.
   1.100 +   *
   1.101 +   * @see devtools/framework/target.js for details about targets.
   1.102 +   *
   1.103 +   * @param object aTarget
   1.104 +   *        The target that the browser console will connect to.
   1.105 +   * @param nsIDOMWindow aIframeWindow
   1.106 +   *        The window where the browser console UI is already loaded.
   1.107 +   * @param nsIDOMWindow aChromeWindow
   1.108 +   *        The window of the browser console owner.
   1.109 +   * @return object
   1.110 +   *         A promise object for the opening of the new BrowserConsole instance.
   1.111 +   */
   1.112 +  openBrowserConsole:
   1.113 +  function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow)
   1.114 +  {
   1.115 +    let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow);
   1.116 +    this._browserConsoleID = hud.hudId;
   1.117 +    this.consoles.set(hud.hudId, hud);
   1.118 +    return hud.init();
   1.119 +  },
   1.120 +
   1.121 +  /**
   1.122 +   * Returns the Web Console object associated to a content window.
   1.123 +   *
   1.124 +   * @param nsIDOMWindow aContentWindow
   1.125 +   * @returns object
   1.126 +   */
   1.127 +  getHudByWindow: function HS_getHudByWindow(aContentWindow)
   1.128 +  {
   1.129 +    for (let [hudId, hud] of this.consoles) {
   1.130 +      let target = hud.target;
   1.131 +      if (target && target.tab && target.window === aContentWindow) {
   1.132 +        return hud;
   1.133 +      }
   1.134 +    }
   1.135 +    return null;
   1.136 +  },
   1.137 +
   1.138 +  /**
   1.139 +   * Returns the console instance for a given id.
   1.140 +   *
   1.141 +   * @param string aId
   1.142 +   * @returns Object
   1.143 +   */
   1.144 +  getHudReferenceById: function HS_getHudReferenceById(aId)
   1.145 +  {
   1.146 +    return this.consoles.get(aId);
   1.147 +  },
   1.148 +
   1.149 +  /**
   1.150 +   * Find if there is a Web Console open for the current tab and return the
   1.151 +   * instance.
   1.152 +   * @return object|null
   1.153 +   *         The WebConsole object or null if the active tab has no open Web
   1.154 +   *         Console.
   1.155 +   */
   1.156 +  getOpenWebConsole: function HS_getOpenWebConsole()
   1.157 +  {
   1.158 +    let tab = this.currentContext().gBrowser.selectedTab;
   1.159 +    if (!tab || !devtools.TargetFactory.isKnownTab(tab)) {
   1.160 +      return null;
   1.161 +    }
   1.162 +    let target = devtools.TargetFactory.forTab(tab);
   1.163 +    let toolbox = gDevTools.getToolbox(target);
   1.164 +    let panel = toolbox ? toolbox.getPanel("webconsole") : null;
   1.165 +    return panel ? panel.hud : null;
   1.166 +  },
   1.167 +
   1.168 +  /**
   1.169 +   * Toggle the Browser Console.
   1.170 +   */
   1.171 +  toggleBrowserConsole: function HS_toggleBrowserConsole()
   1.172 +  {
   1.173 +    if (this._browserConsoleID) {
   1.174 +      let hud = this.getHudReferenceById(this._browserConsoleID);
   1.175 +      return hud.destroy();
   1.176 +    }
   1.177 +
   1.178 +    if (this._browserConsoleDefer) {
   1.179 +      return this._browserConsoleDefer.promise;
   1.180 +    }
   1.181 +
   1.182 +    this._browserConsoleDefer = promise.defer();
   1.183 +
   1.184 +    function connect()
   1.185 +    {
   1.186 +      let deferred = promise.defer();
   1.187 +
   1.188 +      if (!DebuggerServer.initialized) {
   1.189 +        DebuggerServer.init();
   1.190 +        DebuggerServer.addBrowserActors();
   1.191 +      }
   1.192 +
   1.193 +      let client = new DebuggerClient(DebuggerServer.connectPipe());
   1.194 +      client.connect(() =>
   1.195 +        client.listTabs((aResponse) => {
   1.196 +          // Add Global Process debugging...
   1.197 +          let globals = JSON.parse(JSON.stringify(aResponse));
   1.198 +          delete globals.tabs;
   1.199 +          delete globals.selected;
   1.200 +          // ...only if there are appropriate actors (a 'from' property will
   1.201 +          // always be there).
   1.202 +          if (Object.keys(globals).length > 1) {
   1.203 +            deferred.resolve({ form: globals, client: client, chrome: true });
   1.204 +          } else {
   1.205 +            deferred.reject("Global console not found!");
   1.206 +          }
   1.207 +        }));
   1.208 +
   1.209 +      return deferred.promise;
   1.210 +    }
   1.211 +
   1.212 +    let target;
   1.213 +    function getTarget(aConnection)
   1.214 +    {
   1.215 +      let options = {
   1.216 +        form: aConnection.form,
   1.217 +        client: aConnection.client,
   1.218 +        chrome: true,
   1.219 +      };
   1.220 +
   1.221 +      return devtools.TargetFactory.forRemoteTab(options);
   1.222 +    }
   1.223 +
   1.224 +    function openWindow(aTarget)
   1.225 +    {
   1.226 +      target = aTarget;
   1.227 +
   1.228 +      let deferred = promise.defer();
   1.229 +
   1.230 +      let win = Services.ww.openWindow(null, devtools.Tools.webConsole.url, "_blank",
   1.231 +                                       BROWSER_CONSOLE_WINDOW_FEATURES, null);
   1.232 +      win.addEventListener("DOMContentLoaded", function onLoad() {
   1.233 +        win.removeEventListener("DOMContentLoaded", onLoad);
   1.234 +
   1.235 +        // Set the correct Browser Console title.
   1.236 +        let root = win.document.documentElement;
   1.237 +        root.setAttribute("title", root.getAttribute("browserConsoleTitle"));
   1.238 +
   1.239 +        deferred.resolve(win);
   1.240 +      });
   1.241 +
   1.242 +      return deferred.promise;
   1.243 +    }
   1.244 +
   1.245 +    connect().then(getTarget).then(openWindow).then((aWindow) => {
   1.246 +      this.openBrowserConsole(target, aWindow, aWindow)
   1.247 +        .then((aBrowserConsole) => {
   1.248 +          this._browserConsoleDefer.resolve(aBrowserConsole);
   1.249 +          this._browserConsoleDefer = null;
   1.250 +        })
   1.251 +    }, console.error);
   1.252 +
   1.253 +    return this._browserConsoleDefer.promise;
   1.254 +  },
   1.255 +
   1.256 +  /**
   1.257 +   * Get the Browser Console instance, if open.
   1.258 +   *
   1.259 +   * @return object|null
   1.260 +   *         A BrowserConsole instance or null if the Browser Console is not
   1.261 +   *         open.
   1.262 +   */
   1.263 +  getBrowserConsole: function HS_getBrowserConsole()
   1.264 +  {
   1.265 +    return this.getHudReferenceById(this._browserConsoleID);
   1.266 +  },
   1.267 +};
   1.268 +
   1.269 +
   1.270 +/**
   1.271 + * A WebConsole instance is an interactive console initialized *per target*
   1.272 + * that displays console log data as well as provides an interactive terminal to
   1.273 + * manipulate the target's document content.
   1.274 + *
   1.275 + * This object only wraps the iframe that holds the Web Console UI. This is
   1.276 + * meant to be an integration point between the Firefox UI and the Web Console
   1.277 + * UI and features.
   1.278 + *
   1.279 + * @constructor
   1.280 + * @param object aTarget
   1.281 + *        The target that the web console will connect to.
   1.282 + * @param nsIDOMWindow aIframeWindow
   1.283 + *        The window where the web console UI is already loaded.
   1.284 + * @param nsIDOMWindow aChromeWindow
   1.285 + *        The window of the web console owner.
   1.286 + */
   1.287 +function WebConsole(aTarget, aIframeWindow, aChromeWindow)
   1.288 +{
   1.289 +  this.iframeWindow = aIframeWindow;
   1.290 +  this.chromeWindow = aChromeWindow;
   1.291 +  this.hudId = "hud_" + ++gHudId;
   1.292 +  this.target = aTarget;
   1.293 +
   1.294 +  this.browserWindow = this.chromeWindow.top;
   1.295 +
   1.296 +  let element = this.browserWindow.document.documentElement;
   1.297 +  if (element.getAttribute("windowtype") != "navigator:browser") {
   1.298 +    this.browserWindow = HUDService.currentContext();
   1.299 +  }
   1.300 +
   1.301 +  this.ui = new WebConsoleFrame(this);
   1.302 +}
   1.303 +
   1.304 +WebConsole.prototype = {
   1.305 +  iframeWindow: null,
   1.306 +  chromeWindow: null,
   1.307 +  browserWindow: null,
   1.308 +  hudId: null,
   1.309 +  target: null,
   1.310 +  ui: null,
   1.311 +  _browserConsole: false,
   1.312 +  _destroyer: null,
   1.313 +
   1.314 +  /**
   1.315 +   * Getter for a function to to listen for every request that completes. Used
   1.316 +   * by unit tests. The callback takes one argument: the HTTP activity object as
   1.317 +   * received from the remote Web Console.
   1.318 +   *
   1.319 +   * @type function
   1.320 +   */
   1.321 +  get lastFinishedRequestCallback() HUDService.lastFinishedRequest.callback,
   1.322 +
   1.323 +  /**
   1.324 +   * Getter for the window that can provide various utilities that the web
   1.325 +   * console makes use of, like opening links, managing popups, etc.  In
   1.326 +   * most cases, this will be |this.browserWindow|, but in some uses (such as
   1.327 +   * the Browser Toolbox), there is no browser window, so an alternative window
   1.328 +   * hosts the utilities there.
   1.329 +   * @type nsIDOMWindow
   1.330 +   */
   1.331 +  get chromeUtilsWindow()
   1.332 +  {
   1.333 +    if (this.browserWindow) {
   1.334 +      return this.browserWindow;
   1.335 +    }
   1.336 +    return this.chromeWindow.top;
   1.337 +  },
   1.338 +
   1.339 +  /**
   1.340 +   * Getter for the xul:popupset that holds any popups we open.
   1.341 +   * @type nsIDOMElement
   1.342 +   */
   1.343 +  get mainPopupSet()
   1.344 +  {
   1.345 +    return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
   1.346 +  },
   1.347 +
   1.348 +  /**
   1.349 +   * Getter for the output element that holds messages we display.
   1.350 +   * @type nsIDOMElement
   1.351 +   */
   1.352 +  get outputNode()
   1.353 +  {
   1.354 +    return this.ui ? this.ui.outputNode : null;
   1.355 +  },
   1.356 +
   1.357 +  get gViewSourceUtils()
   1.358 +  {
   1.359 +    return this.chromeUtilsWindow.gViewSourceUtils;
   1.360 +  },
   1.361 +
   1.362 +  /**
   1.363 +   * Initialize the Web Console instance.
   1.364 +   *
   1.365 +   * @return object
   1.366 +   *         A promise for the initialization.
   1.367 +   */
   1.368 +  init: function WC_init()
   1.369 +  {
   1.370 +    return this.ui.init().then(() => this);
   1.371 +  },
   1.372 +
   1.373 +  /**
   1.374 +   * Retrieve the Web Console panel title.
   1.375 +   *
   1.376 +   * @return string
   1.377 +   *         The Web Console panel title.
   1.378 +   */
   1.379 +  getPanelTitle: function WC_getPanelTitle()
   1.380 +  {
   1.381 +    let url = this.ui ? this.ui.contentLocation : "";
   1.382 +    return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
   1.383 +  },
   1.384 +
   1.385 +  /**
   1.386 +   * The JSTerm object that manages the console's input.
   1.387 +   * @see webconsole.js::JSTerm
   1.388 +   * @type object
   1.389 +   */
   1.390 +  get jsterm()
   1.391 +  {
   1.392 +    return this.ui ? this.ui.jsterm : null;
   1.393 +  },
   1.394 +
   1.395 +  /**
   1.396 +   * The clear output button handler.
   1.397 +   * @private
   1.398 +   */
   1.399 +  _onClearButton: function WC__onClearButton()
   1.400 +  {
   1.401 +    if (this.target.isLocalTab) {
   1.402 +      this.browserWindow.DeveloperToolbar.resetErrorsCount(this.target.tab);
   1.403 +    }
   1.404 +  },
   1.405 +
   1.406 +  /**
   1.407 +   * Alias for the WebConsoleFrame.setFilterState() method.
   1.408 +   * @see webconsole.js::WebConsoleFrame.setFilterState()
   1.409 +   */
   1.410 +  setFilterState: function WC_setFilterState()
   1.411 +  {
   1.412 +    this.ui && this.ui.setFilterState.apply(this.ui, arguments);
   1.413 +  },
   1.414 +
   1.415 +  /**
   1.416 +   * Open a link in a new tab.
   1.417 +   *
   1.418 +   * @param string aLink
   1.419 +   *        The URL you want to open in a new tab.
   1.420 +   */
   1.421 +  openLink: function WC_openLink(aLink)
   1.422 +  {
   1.423 +    this.chromeUtilsWindow.openUILinkIn(aLink, "tab");
   1.424 +  },
   1.425 +
   1.426 +  /**
   1.427 +   * Open a link in Firefox's view source.
   1.428 +   *
   1.429 +   * @param string aSourceURL
   1.430 +   *        The URL of the file.
   1.431 +   * @param integer aSourceLine
   1.432 +   *        The line number which should be highlighted.
   1.433 +   */
   1.434 +  viewSource: function WC_viewSource(aSourceURL, aSourceLine)
   1.435 +  {
   1.436 +    this.gViewSourceUtils.viewSource(aSourceURL, null,
   1.437 +                                     this.iframeWindow.document, aSourceLine);
   1.438 +  },
   1.439 +
   1.440 +  /**
   1.441 +   * Tries to open a Stylesheet file related to the web page for the web console
   1.442 +   * instance in the Style Editor. If the file is not found, it is opened in
   1.443 +   * source view instead.
   1.444 +   *
   1.445 +   * @param string aSourceURL
   1.446 +   *        The URL of the file.
   1.447 +   * @param integer aSourceLine
   1.448 +   *        The line number which you want to place the caret.
   1.449 +   * TODO: This function breaks the client-server boundaries.
   1.450 +   *       To be fixed in bug 793259.
   1.451 +   */
   1.452 +  viewSourceInStyleEditor:
   1.453 +  function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine)
   1.454 +  {
   1.455 +    let toolbox = gDevTools.getToolbox(this.target);
   1.456 +    if (!toolbox) {
   1.457 +      this.viewSource(aSourceURL, aSourceLine);
   1.458 +      return;
   1.459 +    }
   1.460 +
   1.461 +    gDevTools.showToolbox(this.target, "styleeditor").then(function(toolbox) {
   1.462 +      try {
   1.463 +        toolbox.getCurrentPanel().selectStyleSheet(aSourceURL, aSourceLine);
   1.464 +      } catch(e) {
   1.465 +        // Open view source if style editor fails.
   1.466 +        this.viewSource(aSourceURL, aSourceLine);
   1.467 +      }
   1.468 +    });
   1.469 +  },
   1.470 +
   1.471 +  /**
   1.472 +   * Tries to open a JavaScript file related to the web page for the web console
   1.473 +   * instance in the Script Debugger. If the file is not found, it is opened in
   1.474 +   * source view instead.
   1.475 +   *
   1.476 +   * @param string aSourceURL
   1.477 +   *        The URL of the file.
   1.478 +   * @param integer aSourceLine
   1.479 +   *        The line number which you want to place the caret.
   1.480 +   */
   1.481 +  viewSourceInDebugger:
   1.482 +  function WC_viewSourceInDebugger(aSourceURL, aSourceLine)
   1.483 +  {
   1.484 +    let toolbox = gDevTools.getToolbox(this.target);
   1.485 +    if (!toolbox) {
   1.486 +      this.viewSource(aSourceURL, aSourceLine);
   1.487 +      return;
   1.488 +    }
   1.489 +
   1.490 +    let showSource = ({ DebuggerView }) => {
   1.491 +      if (DebuggerView.Sources.containsValue(aSourceURL)) {
   1.492 +        DebuggerView.setEditorLocation(aSourceURL, aSourceLine,
   1.493 +                                       { noDebug: true }).then(() => {
   1.494 +          this.ui.emit("source-in-debugger-opened");
   1.495 +        });
   1.496 +        return;
   1.497 +      }
   1.498 +      toolbox.selectTool("webconsole");
   1.499 +      this.viewSource(aSourceURL, aSourceLine);
   1.500 +    }
   1.501 +
   1.502 +    // If the Debugger was already open, switch to it and try to show the
   1.503 +    // source immediately. Otherwise, initialize it and wait for the sources
   1.504 +    // to be added first.
   1.505 +    let debuggerAlreadyOpen = toolbox.getPanel("jsdebugger");
   1.506 +    toolbox.selectTool("jsdebugger").then(({ panelWin: dbg }) => {
   1.507 +      if (debuggerAlreadyOpen) {
   1.508 +        showSource(dbg);
   1.509 +      } else {
   1.510 +        dbg.once(dbg.EVENTS.SOURCES_ADDED, () => showSource(dbg));
   1.511 +      }
   1.512 +    });
   1.513 +  },
   1.514 +
   1.515 +
   1.516 +  /**
   1.517 +   * Tries to open a JavaScript file related to the web page for the web console
   1.518 +   * instance in the corresponding Scratchpad.
   1.519 +   *
   1.520 +   * @param string aSourceURL
   1.521 +   *        The URL of the file which corresponds to a Scratchpad id.
   1.522 +   */
   1.523 +  viewSourceInScratchpad: function WC_viewSourceInScratchpad(aSourceURL)
   1.524 +  {
   1.525 +    // Check for matching top level Scratchpad window.
   1.526 +    let wins = Services.wm.getEnumerator("devtools:scratchpad");
   1.527 +
   1.528 +    while (wins.hasMoreElements()) {
   1.529 +      let win = wins.getNext();
   1.530 +
   1.531 +      if (!win.closed && win.Scratchpad.uniqueName === aSourceURL) {
   1.532 +        win.focus();
   1.533 +        return;
   1.534 +      }
   1.535 +    }
   1.536 +
   1.537 +    // Check for matching Scratchpad toolbox tab.
   1.538 +    for (let [, toolbox] of gDevTools) {
   1.539 +      let scratchpadPanel = toolbox.getPanel("scratchpad");
   1.540 +      if (scratchpadPanel) {
   1.541 +        let { scratchpad } = scratchpadPanel;
   1.542 +        if (scratchpad.uniqueName === aSourceURL) {
   1.543 +          toolbox.selectTool("scratchpad");
   1.544 +          toolbox.raise();
   1.545 +          scratchpad.editor.focus();
   1.546 +          return;
   1.547 +        }
   1.548 +      }
   1.549 +    }
   1.550 +  },
   1.551 +
   1.552 +  /**
   1.553 +   * Retrieve information about the JavaScript debugger's stackframes list. This
   1.554 +   * is used to allow the Web Console to evaluate code in the selected
   1.555 +   * stackframe.
   1.556 +   *
   1.557 +   * @return object|null
   1.558 +   *         An object which holds:
   1.559 +   *         - frames: the active ThreadClient.cachedFrames array.
   1.560 +   *         - selected: depth/index of the selected stackframe in the debugger
   1.561 +   *         UI.
   1.562 +   *         If the debugger is not open or if it's not paused, then |null| is
   1.563 +   *         returned.
   1.564 +   */
   1.565 +  getDebuggerFrames: function WC_getDebuggerFrames()
   1.566 +  {
   1.567 +    let toolbox = gDevTools.getToolbox(this.target);
   1.568 +    if (!toolbox) {
   1.569 +      return null;
   1.570 +    }
   1.571 +    let panel = toolbox.getPanel("jsdebugger");
   1.572 +    if (!panel) {
   1.573 +      return null;
   1.574 +    }
   1.575 +    let framesController = panel.panelWin.DebuggerController.StackFrames;
   1.576 +    let thread = framesController.activeThread;
   1.577 +    if (thread && thread.paused) {
   1.578 +      return {
   1.579 +        frames: thread.cachedFrames,
   1.580 +        selected: framesController.currentFrameDepth,
   1.581 +      };
   1.582 +    }
   1.583 +    return null;
   1.584 +  },
   1.585 +
   1.586 +  /**
   1.587 +   * Destroy the object. Call this method to avoid memory leaks when the Web
   1.588 +   * Console is closed.
   1.589 +   *
   1.590 +   * @return object
   1.591 +   *         A promise object that is resolved once the Web Console is closed.
   1.592 +   */
   1.593 +  destroy: function WC_destroy()
   1.594 +  {
   1.595 +    if (this._destroyer) {
   1.596 +      return this._destroyer.promise;
   1.597 +    }
   1.598 +
   1.599 +    HUDService.consoles.delete(this.hudId);
   1.600 +
   1.601 +    this._destroyer = promise.defer();
   1.602 +
   1.603 +    let popupset = this.mainPopupSet;
   1.604 +    let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
   1.605 +    for (let panel of panels) {
   1.606 +      panel.hidePopup();
   1.607 +    }
   1.608 +
   1.609 +    let onDestroy = function WC_onDestroyUI() {
   1.610 +      try {
   1.611 +        let tabWindow = this.target.isLocalTab ? this.target.window : null;
   1.612 +        tabWindow && tabWindow.focus();
   1.613 +      }
   1.614 +      catch (ex) {
   1.615 +        // Tab focus can fail if the tab or target is closed.
   1.616 +      }
   1.617 +
   1.618 +      let id = WebConsoleUtils.supportsString(this.hudId);
   1.619 +      Services.obs.notifyObservers(id, "web-console-destroyed", null);
   1.620 +      this._destroyer.resolve(null);
   1.621 +    }.bind(this);
   1.622 +
   1.623 +    if (this.ui) {
   1.624 +      this.ui.destroy().then(onDestroy);
   1.625 +    }
   1.626 +    else {
   1.627 +      onDestroy();
   1.628 +    }
   1.629 +
   1.630 +    return this._destroyer.promise;
   1.631 +  },
   1.632 +};
   1.633 +
   1.634 +
   1.635 +/**
   1.636 + * A BrowserConsole instance is an interactive console initialized *per target*
   1.637 + * that displays console log data as well as provides an interactive terminal to
   1.638 + * manipulate the target's document content.
   1.639 + *
   1.640 + * This object only wraps the iframe that holds the Browser Console UI. This is
   1.641 + * meant to be an integration point between the Firefox UI and the Browser Console
   1.642 + * UI and features.
   1.643 + *
   1.644 + * @constructor
   1.645 + * @param object aTarget
   1.646 + *        The target that the browser console will connect to.
   1.647 + * @param nsIDOMWindow aIframeWindow
   1.648 + *        The window where the browser console UI is already loaded.
   1.649 + * @param nsIDOMWindow aChromeWindow
   1.650 + *        The window of the browser console owner.
   1.651 + */
   1.652 +function BrowserConsole()
   1.653 +{
   1.654 +  WebConsole.apply(this, arguments);
   1.655 +  this._telemetry = new Telemetry();
   1.656 +}
   1.657 +
   1.658 +BrowserConsole.prototype = Heritage.extend(WebConsole.prototype,
   1.659 +{
   1.660 +  _browserConsole: true,
   1.661 +  _bc_init: null,
   1.662 +  _bc_destroyer: null,
   1.663 +
   1.664 +  $init: WebConsole.prototype.init,
   1.665 +
   1.666 +  /**
   1.667 +   * Initialize the Browser Console instance.
   1.668 +   *
   1.669 +   * @return object
   1.670 +   *         A promise for the initialization.
   1.671 +   */
   1.672 +  init: function BC_init()
   1.673 +  {
   1.674 +    if (this._bc_init) {
   1.675 +      return this._bc_init;
   1.676 +    }
   1.677 +
   1.678 +    this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
   1.679 +
   1.680 +    let window = this.iframeWindow;
   1.681 +
   1.682 +    // Make sure that the closing of the Browser Console window destroys this
   1.683 +    // instance.
   1.684 +    let onClose = () => {
   1.685 +      window.removeEventListener("unload", onClose);
   1.686 +      this.destroy();
   1.687 +    };
   1.688 +    window.addEventListener("unload", onClose);
   1.689 +
   1.690 +    // Make sure Ctrl-W closes the Browser Console window.
   1.691 +    window.document.getElementById("cmd_close").removeAttribute("disabled");
   1.692 +
   1.693 +    this._telemetry.toolOpened("browserconsole");
   1.694 +
   1.695 +    this._bc_init = this.$init();
   1.696 +    return this._bc_init;
   1.697 +  },
   1.698 +
   1.699 +  $destroy: WebConsole.prototype.destroy,
   1.700 +
   1.701 +  /**
   1.702 +   * Destroy the object.
   1.703 +   *
   1.704 +   * @return object
   1.705 +   *         A promise object that is resolved once the Browser Console is closed.
   1.706 +   */
   1.707 +  destroy: function BC_destroy()
   1.708 +  {
   1.709 +    if (this._bc_destroyer) {
   1.710 +      return this._bc_destroyer.promise;
   1.711 +    }
   1.712 +
   1.713 +    this._telemetry.toolClosed("browserconsole");
   1.714 +
   1.715 +    this._bc_destroyer = promise.defer();
   1.716 +
   1.717 +    let chromeWindow = this.chromeWindow;
   1.718 +    this.$destroy().then(() =>
   1.719 +      this.target.client.close(() => {
   1.720 +        HUDService._browserConsoleID = null;
   1.721 +        chromeWindow.close();
   1.722 +        this._bc_destroyer.resolve(null);
   1.723 +      }));
   1.724 +
   1.725 +    return this._bc_destroyer.promise;
   1.726 +  },
   1.727 +});
   1.728 +
   1.729 +const HUDService = new HUD_SERVICE();
   1.730 +
   1.731 +(() => {
   1.732 +  let methods = ["openWebConsole", "openBrowserConsole",
   1.733 +                 "toggleBrowserConsole", "getOpenWebConsole",
   1.734 +                 "getBrowserConsole", "getHudByWindow", "getHudReferenceById"];
   1.735 +  for (let method of methods) {
   1.736 +    exports[method] = HUDService[method].bind(HUDService);
   1.737 +  }
   1.738 +
   1.739 +  exports.consoles = HUDService.consoles;
   1.740 +  exports.lastFinishedRequest = HUDService.lastFinishedRequest;
   1.741 +})();

mercurial