browser/devtools/webconsole/hudservice.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
michael@0 2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 "use strict";
michael@0 8
michael@0 9 const {Cc, Ci, Cu} = require("chrome");
michael@0 10
michael@0 11 let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
michael@0 12 let Heritage = require("sdk/core/heritage");
michael@0 13
michael@0 14 loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry"));
michael@0 15 loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame);
michael@0 16 loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
michael@0 17 loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
michael@0 18 loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm");
michael@0 19 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
michael@0 20 loader.lazyImporter(this, "DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
michael@0 21 loader.lazyImporter(this, "DebuggerClient", "resource://gre/modules/devtools/dbg-client.jsm");
michael@0 22
michael@0 23 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
michael@0 24 let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
michael@0 25
michael@0 26 const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
michael@0 27
michael@0 28 // The preference prefix for all of the Browser Console filters.
michael@0 29 const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
michael@0 30
michael@0 31 let gHudId = 0;
michael@0 32
michael@0 33 ///////////////////////////////////////////////////////////////////////////
michael@0 34 //// The HUD service
michael@0 35
michael@0 36 function HUD_SERVICE()
michael@0 37 {
michael@0 38 this.consoles = new Map();
michael@0 39 this.lastFinishedRequest = { callback: null };
michael@0 40 }
michael@0 41
michael@0 42 HUD_SERVICE.prototype =
michael@0 43 {
michael@0 44 _browserConsoleID: null,
michael@0 45 _browserConsoleDefer: null,
michael@0 46
michael@0 47 /**
michael@0 48 * Keeps a reference for each Web Console / Browser Console that is created.
michael@0 49 * @type Map
michael@0 50 */
michael@0 51 consoles: null,
michael@0 52
michael@0 53 /**
michael@0 54 * Assign a function to this property to listen for every request that
michael@0 55 * completes. Used by unit tests. The callback takes one argument: the HTTP
michael@0 56 * activity object as received from the remote Web Console.
michael@0 57 *
michael@0 58 * @type object
michael@0 59 * Includes a property named |callback|. Assign the function to the
michael@0 60 * |callback| property of this object.
michael@0 61 */
michael@0 62 lastFinishedRequest: null,
michael@0 63
michael@0 64 /**
michael@0 65 * Firefox-specific current tab getter
michael@0 66 *
michael@0 67 * @returns nsIDOMWindow
michael@0 68 */
michael@0 69 currentContext: function HS_currentContext() {
michael@0 70 return Services.wm.getMostRecentWindow("navigator:browser");
michael@0 71 },
michael@0 72
michael@0 73 /**
michael@0 74 * Open a Web Console for the given target.
michael@0 75 *
michael@0 76 * @see devtools/framework/target.js for details about targets.
michael@0 77 *
michael@0 78 * @param object aTarget
michael@0 79 * The target that the web console will connect to.
michael@0 80 * @param nsIDOMWindow aIframeWindow
michael@0 81 * The window where the web console UI is already loaded.
michael@0 82 * @param nsIDOMWindow aChromeWindow
michael@0 83 * The window of the web console owner.
michael@0 84 * @return object
michael@0 85 * A promise object for the opening of the new WebConsole instance.
michael@0 86 */
michael@0 87 openWebConsole:
michael@0 88 function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
michael@0 89 {
michael@0 90 let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
michael@0 91 this.consoles.set(hud.hudId, hud);
michael@0 92 return hud.init();
michael@0 93 },
michael@0 94
michael@0 95 /**
michael@0 96 * Open a Browser Console for the given target.
michael@0 97 *
michael@0 98 * @see devtools/framework/target.js for details about targets.
michael@0 99 *
michael@0 100 * @param object aTarget
michael@0 101 * The target that the browser console will connect to.
michael@0 102 * @param nsIDOMWindow aIframeWindow
michael@0 103 * The window where the browser console UI is already loaded.
michael@0 104 * @param nsIDOMWindow aChromeWindow
michael@0 105 * The window of the browser console owner.
michael@0 106 * @return object
michael@0 107 * A promise object for the opening of the new BrowserConsole instance.
michael@0 108 */
michael@0 109 openBrowserConsole:
michael@0 110 function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow)
michael@0 111 {
michael@0 112 let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow);
michael@0 113 this._browserConsoleID = hud.hudId;
michael@0 114 this.consoles.set(hud.hudId, hud);
michael@0 115 return hud.init();
michael@0 116 },
michael@0 117
michael@0 118 /**
michael@0 119 * Returns the Web Console object associated to a content window.
michael@0 120 *
michael@0 121 * @param nsIDOMWindow aContentWindow
michael@0 122 * @returns object
michael@0 123 */
michael@0 124 getHudByWindow: function HS_getHudByWindow(aContentWindow)
michael@0 125 {
michael@0 126 for (let [hudId, hud] of this.consoles) {
michael@0 127 let target = hud.target;
michael@0 128 if (target && target.tab && target.window === aContentWindow) {
michael@0 129 return hud;
michael@0 130 }
michael@0 131 }
michael@0 132 return null;
michael@0 133 },
michael@0 134
michael@0 135 /**
michael@0 136 * Returns the console instance for a given id.
michael@0 137 *
michael@0 138 * @param string aId
michael@0 139 * @returns Object
michael@0 140 */
michael@0 141 getHudReferenceById: function HS_getHudReferenceById(aId)
michael@0 142 {
michael@0 143 return this.consoles.get(aId);
michael@0 144 },
michael@0 145
michael@0 146 /**
michael@0 147 * Find if there is a Web Console open for the current tab and return the
michael@0 148 * instance.
michael@0 149 * @return object|null
michael@0 150 * The WebConsole object or null if the active tab has no open Web
michael@0 151 * Console.
michael@0 152 */
michael@0 153 getOpenWebConsole: function HS_getOpenWebConsole()
michael@0 154 {
michael@0 155 let tab = this.currentContext().gBrowser.selectedTab;
michael@0 156 if (!tab || !devtools.TargetFactory.isKnownTab(tab)) {
michael@0 157 return null;
michael@0 158 }
michael@0 159 let target = devtools.TargetFactory.forTab(tab);
michael@0 160 let toolbox = gDevTools.getToolbox(target);
michael@0 161 let panel = toolbox ? toolbox.getPanel("webconsole") : null;
michael@0 162 return panel ? panel.hud : null;
michael@0 163 },
michael@0 164
michael@0 165 /**
michael@0 166 * Toggle the Browser Console.
michael@0 167 */
michael@0 168 toggleBrowserConsole: function HS_toggleBrowserConsole()
michael@0 169 {
michael@0 170 if (this._browserConsoleID) {
michael@0 171 let hud = this.getHudReferenceById(this._browserConsoleID);
michael@0 172 return hud.destroy();
michael@0 173 }
michael@0 174
michael@0 175 if (this._browserConsoleDefer) {
michael@0 176 return this._browserConsoleDefer.promise;
michael@0 177 }
michael@0 178
michael@0 179 this._browserConsoleDefer = promise.defer();
michael@0 180
michael@0 181 function connect()
michael@0 182 {
michael@0 183 let deferred = promise.defer();
michael@0 184
michael@0 185 if (!DebuggerServer.initialized) {
michael@0 186 DebuggerServer.init();
michael@0 187 DebuggerServer.addBrowserActors();
michael@0 188 }
michael@0 189
michael@0 190 let client = new DebuggerClient(DebuggerServer.connectPipe());
michael@0 191 client.connect(() =>
michael@0 192 client.listTabs((aResponse) => {
michael@0 193 // Add Global Process debugging...
michael@0 194 let globals = JSON.parse(JSON.stringify(aResponse));
michael@0 195 delete globals.tabs;
michael@0 196 delete globals.selected;
michael@0 197 // ...only if there are appropriate actors (a 'from' property will
michael@0 198 // always be there).
michael@0 199 if (Object.keys(globals).length > 1) {
michael@0 200 deferred.resolve({ form: globals, client: client, chrome: true });
michael@0 201 } else {
michael@0 202 deferred.reject("Global console not found!");
michael@0 203 }
michael@0 204 }));
michael@0 205
michael@0 206 return deferred.promise;
michael@0 207 }
michael@0 208
michael@0 209 let target;
michael@0 210 function getTarget(aConnection)
michael@0 211 {
michael@0 212 let options = {
michael@0 213 form: aConnection.form,
michael@0 214 client: aConnection.client,
michael@0 215 chrome: true,
michael@0 216 };
michael@0 217
michael@0 218 return devtools.TargetFactory.forRemoteTab(options);
michael@0 219 }
michael@0 220
michael@0 221 function openWindow(aTarget)
michael@0 222 {
michael@0 223 target = aTarget;
michael@0 224
michael@0 225 let deferred = promise.defer();
michael@0 226
michael@0 227 let win = Services.ww.openWindow(null, devtools.Tools.webConsole.url, "_blank",
michael@0 228 BROWSER_CONSOLE_WINDOW_FEATURES, null);
michael@0 229 win.addEventListener("DOMContentLoaded", function onLoad() {
michael@0 230 win.removeEventListener("DOMContentLoaded", onLoad);
michael@0 231
michael@0 232 // Set the correct Browser Console title.
michael@0 233 let root = win.document.documentElement;
michael@0 234 root.setAttribute("title", root.getAttribute("browserConsoleTitle"));
michael@0 235
michael@0 236 deferred.resolve(win);
michael@0 237 });
michael@0 238
michael@0 239 return deferred.promise;
michael@0 240 }
michael@0 241
michael@0 242 connect().then(getTarget).then(openWindow).then((aWindow) => {
michael@0 243 this.openBrowserConsole(target, aWindow, aWindow)
michael@0 244 .then((aBrowserConsole) => {
michael@0 245 this._browserConsoleDefer.resolve(aBrowserConsole);
michael@0 246 this._browserConsoleDefer = null;
michael@0 247 })
michael@0 248 }, console.error);
michael@0 249
michael@0 250 return this._browserConsoleDefer.promise;
michael@0 251 },
michael@0 252
michael@0 253 /**
michael@0 254 * Get the Browser Console instance, if open.
michael@0 255 *
michael@0 256 * @return object|null
michael@0 257 * A BrowserConsole instance or null if the Browser Console is not
michael@0 258 * open.
michael@0 259 */
michael@0 260 getBrowserConsole: function HS_getBrowserConsole()
michael@0 261 {
michael@0 262 return this.getHudReferenceById(this._browserConsoleID);
michael@0 263 },
michael@0 264 };
michael@0 265
michael@0 266
michael@0 267 /**
michael@0 268 * A WebConsole instance is an interactive console initialized *per target*
michael@0 269 * that displays console log data as well as provides an interactive terminal to
michael@0 270 * manipulate the target's document content.
michael@0 271 *
michael@0 272 * This object only wraps the iframe that holds the Web Console UI. This is
michael@0 273 * meant to be an integration point between the Firefox UI and the Web Console
michael@0 274 * UI and features.
michael@0 275 *
michael@0 276 * @constructor
michael@0 277 * @param object aTarget
michael@0 278 * The target that the web console will connect to.
michael@0 279 * @param nsIDOMWindow aIframeWindow
michael@0 280 * The window where the web console UI is already loaded.
michael@0 281 * @param nsIDOMWindow aChromeWindow
michael@0 282 * The window of the web console owner.
michael@0 283 */
michael@0 284 function WebConsole(aTarget, aIframeWindow, aChromeWindow)
michael@0 285 {
michael@0 286 this.iframeWindow = aIframeWindow;
michael@0 287 this.chromeWindow = aChromeWindow;
michael@0 288 this.hudId = "hud_" + ++gHudId;
michael@0 289 this.target = aTarget;
michael@0 290
michael@0 291 this.browserWindow = this.chromeWindow.top;
michael@0 292
michael@0 293 let element = this.browserWindow.document.documentElement;
michael@0 294 if (element.getAttribute("windowtype") != "navigator:browser") {
michael@0 295 this.browserWindow = HUDService.currentContext();
michael@0 296 }
michael@0 297
michael@0 298 this.ui = new WebConsoleFrame(this);
michael@0 299 }
michael@0 300
michael@0 301 WebConsole.prototype = {
michael@0 302 iframeWindow: null,
michael@0 303 chromeWindow: null,
michael@0 304 browserWindow: null,
michael@0 305 hudId: null,
michael@0 306 target: null,
michael@0 307 ui: null,
michael@0 308 _browserConsole: false,
michael@0 309 _destroyer: null,
michael@0 310
michael@0 311 /**
michael@0 312 * Getter for a function to to listen for every request that completes. Used
michael@0 313 * by unit tests. The callback takes one argument: the HTTP activity object as
michael@0 314 * received from the remote Web Console.
michael@0 315 *
michael@0 316 * @type function
michael@0 317 */
michael@0 318 get lastFinishedRequestCallback() HUDService.lastFinishedRequest.callback,
michael@0 319
michael@0 320 /**
michael@0 321 * Getter for the window that can provide various utilities that the web
michael@0 322 * console makes use of, like opening links, managing popups, etc. In
michael@0 323 * most cases, this will be |this.browserWindow|, but in some uses (such as
michael@0 324 * the Browser Toolbox), there is no browser window, so an alternative window
michael@0 325 * hosts the utilities there.
michael@0 326 * @type nsIDOMWindow
michael@0 327 */
michael@0 328 get chromeUtilsWindow()
michael@0 329 {
michael@0 330 if (this.browserWindow) {
michael@0 331 return this.browserWindow;
michael@0 332 }
michael@0 333 return this.chromeWindow.top;
michael@0 334 },
michael@0 335
michael@0 336 /**
michael@0 337 * Getter for the xul:popupset that holds any popups we open.
michael@0 338 * @type nsIDOMElement
michael@0 339 */
michael@0 340 get mainPopupSet()
michael@0 341 {
michael@0 342 return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
michael@0 343 },
michael@0 344
michael@0 345 /**
michael@0 346 * Getter for the output element that holds messages we display.
michael@0 347 * @type nsIDOMElement
michael@0 348 */
michael@0 349 get outputNode()
michael@0 350 {
michael@0 351 return this.ui ? this.ui.outputNode : null;
michael@0 352 },
michael@0 353
michael@0 354 get gViewSourceUtils()
michael@0 355 {
michael@0 356 return this.chromeUtilsWindow.gViewSourceUtils;
michael@0 357 },
michael@0 358
michael@0 359 /**
michael@0 360 * Initialize the Web Console instance.
michael@0 361 *
michael@0 362 * @return object
michael@0 363 * A promise for the initialization.
michael@0 364 */
michael@0 365 init: function WC_init()
michael@0 366 {
michael@0 367 return this.ui.init().then(() => this);
michael@0 368 },
michael@0 369
michael@0 370 /**
michael@0 371 * Retrieve the Web Console panel title.
michael@0 372 *
michael@0 373 * @return string
michael@0 374 * The Web Console panel title.
michael@0 375 */
michael@0 376 getPanelTitle: function WC_getPanelTitle()
michael@0 377 {
michael@0 378 let url = this.ui ? this.ui.contentLocation : "";
michael@0 379 return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
michael@0 380 },
michael@0 381
michael@0 382 /**
michael@0 383 * The JSTerm object that manages the console's input.
michael@0 384 * @see webconsole.js::JSTerm
michael@0 385 * @type object
michael@0 386 */
michael@0 387 get jsterm()
michael@0 388 {
michael@0 389 return this.ui ? this.ui.jsterm : null;
michael@0 390 },
michael@0 391
michael@0 392 /**
michael@0 393 * The clear output button handler.
michael@0 394 * @private
michael@0 395 */
michael@0 396 _onClearButton: function WC__onClearButton()
michael@0 397 {
michael@0 398 if (this.target.isLocalTab) {
michael@0 399 this.browserWindow.DeveloperToolbar.resetErrorsCount(this.target.tab);
michael@0 400 }
michael@0 401 },
michael@0 402
michael@0 403 /**
michael@0 404 * Alias for the WebConsoleFrame.setFilterState() method.
michael@0 405 * @see webconsole.js::WebConsoleFrame.setFilterState()
michael@0 406 */
michael@0 407 setFilterState: function WC_setFilterState()
michael@0 408 {
michael@0 409 this.ui && this.ui.setFilterState.apply(this.ui, arguments);
michael@0 410 },
michael@0 411
michael@0 412 /**
michael@0 413 * Open a link in a new tab.
michael@0 414 *
michael@0 415 * @param string aLink
michael@0 416 * The URL you want to open in a new tab.
michael@0 417 */
michael@0 418 openLink: function WC_openLink(aLink)
michael@0 419 {
michael@0 420 this.chromeUtilsWindow.openUILinkIn(aLink, "tab");
michael@0 421 },
michael@0 422
michael@0 423 /**
michael@0 424 * Open a link in Firefox's view source.
michael@0 425 *
michael@0 426 * @param string aSourceURL
michael@0 427 * The URL of the file.
michael@0 428 * @param integer aSourceLine
michael@0 429 * The line number which should be highlighted.
michael@0 430 */
michael@0 431 viewSource: function WC_viewSource(aSourceURL, aSourceLine)
michael@0 432 {
michael@0 433 this.gViewSourceUtils.viewSource(aSourceURL, null,
michael@0 434 this.iframeWindow.document, aSourceLine);
michael@0 435 },
michael@0 436
michael@0 437 /**
michael@0 438 * Tries to open a Stylesheet file related to the web page for the web console
michael@0 439 * instance in the Style Editor. If the file is not found, it is opened in
michael@0 440 * source view instead.
michael@0 441 *
michael@0 442 * @param string aSourceURL
michael@0 443 * The URL of the file.
michael@0 444 * @param integer aSourceLine
michael@0 445 * The line number which you want to place the caret.
michael@0 446 * TODO: This function breaks the client-server boundaries.
michael@0 447 * To be fixed in bug 793259.
michael@0 448 */
michael@0 449 viewSourceInStyleEditor:
michael@0 450 function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine)
michael@0 451 {
michael@0 452 let toolbox = gDevTools.getToolbox(this.target);
michael@0 453 if (!toolbox) {
michael@0 454 this.viewSource(aSourceURL, aSourceLine);
michael@0 455 return;
michael@0 456 }
michael@0 457
michael@0 458 gDevTools.showToolbox(this.target, "styleeditor").then(function(toolbox) {
michael@0 459 try {
michael@0 460 toolbox.getCurrentPanel().selectStyleSheet(aSourceURL, aSourceLine);
michael@0 461 } catch(e) {
michael@0 462 // Open view source if style editor fails.
michael@0 463 this.viewSource(aSourceURL, aSourceLine);
michael@0 464 }
michael@0 465 });
michael@0 466 },
michael@0 467
michael@0 468 /**
michael@0 469 * Tries to open a JavaScript file related to the web page for the web console
michael@0 470 * instance in the Script Debugger. If the file is not found, it is opened in
michael@0 471 * source view instead.
michael@0 472 *
michael@0 473 * @param string aSourceURL
michael@0 474 * The URL of the file.
michael@0 475 * @param integer aSourceLine
michael@0 476 * The line number which you want to place the caret.
michael@0 477 */
michael@0 478 viewSourceInDebugger:
michael@0 479 function WC_viewSourceInDebugger(aSourceURL, aSourceLine)
michael@0 480 {
michael@0 481 let toolbox = gDevTools.getToolbox(this.target);
michael@0 482 if (!toolbox) {
michael@0 483 this.viewSource(aSourceURL, aSourceLine);
michael@0 484 return;
michael@0 485 }
michael@0 486
michael@0 487 let showSource = ({ DebuggerView }) => {
michael@0 488 if (DebuggerView.Sources.containsValue(aSourceURL)) {
michael@0 489 DebuggerView.setEditorLocation(aSourceURL, aSourceLine,
michael@0 490 { noDebug: true }).then(() => {
michael@0 491 this.ui.emit("source-in-debugger-opened");
michael@0 492 });
michael@0 493 return;
michael@0 494 }
michael@0 495 toolbox.selectTool("webconsole");
michael@0 496 this.viewSource(aSourceURL, aSourceLine);
michael@0 497 }
michael@0 498
michael@0 499 // If the Debugger was already open, switch to it and try to show the
michael@0 500 // source immediately. Otherwise, initialize it and wait for the sources
michael@0 501 // to be added first.
michael@0 502 let debuggerAlreadyOpen = toolbox.getPanel("jsdebugger");
michael@0 503 toolbox.selectTool("jsdebugger").then(({ panelWin: dbg }) => {
michael@0 504 if (debuggerAlreadyOpen) {
michael@0 505 showSource(dbg);
michael@0 506 } else {
michael@0 507 dbg.once(dbg.EVENTS.SOURCES_ADDED, () => showSource(dbg));
michael@0 508 }
michael@0 509 });
michael@0 510 },
michael@0 511
michael@0 512
michael@0 513 /**
michael@0 514 * Tries to open a JavaScript file related to the web page for the web console
michael@0 515 * instance in the corresponding Scratchpad.
michael@0 516 *
michael@0 517 * @param string aSourceURL
michael@0 518 * The URL of the file which corresponds to a Scratchpad id.
michael@0 519 */
michael@0 520 viewSourceInScratchpad: function WC_viewSourceInScratchpad(aSourceURL)
michael@0 521 {
michael@0 522 // Check for matching top level Scratchpad window.
michael@0 523 let wins = Services.wm.getEnumerator("devtools:scratchpad");
michael@0 524
michael@0 525 while (wins.hasMoreElements()) {
michael@0 526 let win = wins.getNext();
michael@0 527
michael@0 528 if (!win.closed && win.Scratchpad.uniqueName === aSourceURL) {
michael@0 529 win.focus();
michael@0 530 return;
michael@0 531 }
michael@0 532 }
michael@0 533
michael@0 534 // Check for matching Scratchpad toolbox tab.
michael@0 535 for (let [, toolbox] of gDevTools) {
michael@0 536 let scratchpadPanel = toolbox.getPanel("scratchpad");
michael@0 537 if (scratchpadPanel) {
michael@0 538 let { scratchpad } = scratchpadPanel;
michael@0 539 if (scratchpad.uniqueName === aSourceURL) {
michael@0 540 toolbox.selectTool("scratchpad");
michael@0 541 toolbox.raise();
michael@0 542 scratchpad.editor.focus();
michael@0 543 return;
michael@0 544 }
michael@0 545 }
michael@0 546 }
michael@0 547 },
michael@0 548
michael@0 549 /**
michael@0 550 * Retrieve information about the JavaScript debugger's stackframes list. This
michael@0 551 * is used to allow the Web Console to evaluate code in the selected
michael@0 552 * stackframe.
michael@0 553 *
michael@0 554 * @return object|null
michael@0 555 * An object which holds:
michael@0 556 * - frames: the active ThreadClient.cachedFrames array.
michael@0 557 * - selected: depth/index of the selected stackframe in the debugger
michael@0 558 * UI.
michael@0 559 * If the debugger is not open or if it's not paused, then |null| is
michael@0 560 * returned.
michael@0 561 */
michael@0 562 getDebuggerFrames: function WC_getDebuggerFrames()
michael@0 563 {
michael@0 564 let toolbox = gDevTools.getToolbox(this.target);
michael@0 565 if (!toolbox) {
michael@0 566 return null;
michael@0 567 }
michael@0 568 let panel = toolbox.getPanel("jsdebugger");
michael@0 569 if (!panel) {
michael@0 570 return null;
michael@0 571 }
michael@0 572 let framesController = panel.panelWin.DebuggerController.StackFrames;
michael@0 573 let thread = framesController.activeThread;
michael@0 574 if (thread && thread.paused) {
michael@0 575 return {
michael@0 576 frames: thread.cachedFrames,
michael@0 577 selected: framesController.currentFrameDepth,
michael@0 578 };
michael@0 579 }
michael@0 580 return null;
michael@0 581 },
michael@0 582
michael@0 583 /**
michael@0 584 * Destroy the object. Call this method to avoid memory leaks when the Web
michael@0 585 * Console is closed.
michael@0 586 *
michael@0 587 * @return object
michael@0 588 * A promise object that is resolved once the Web Console is closed.
michael@0 589 */
michael@0 590 destroy: function WC_destroy()
michael@0 591 {
michael@0 592 if (this._destroyer) {
michael@0 593 return this._destroyer.promise;
michael@0 594 }
michael@0 595
michael@0 596 HUDService.consoles.delete(this.hudId);
michael@0 597
michael@0 598 this._destroyer = promise.defer();
michael@0 599
michael@0 600 let popupset = this.mainPopupSet;
michael@0 601 let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
michael@0 602 for (let panel of panels) {
michael@0 603 panel.hidePopup();
michael@0 604 }
michael@0 605
michael@0 606 let onDestroy = function WC_onDestroyUI() {
michael@0 607 try {
michael@0 608 let tabWindow = this.target.isLocalTab ? this.target.window : null;
michael@0 609 tabWindow && tabWindow.focus();
michael@0 610 }
michael@0 611 catch (ex) {
michael@0 612 // Tab focus can fail if the tab or target is closed.
michael@0 613 }
michael@0 614
michael@0 615 let id = WebConsoleUtils.supportsString(this.hudId);
michael@0 616 Services.obs.notifyObservers(id, "web-console-destroyed", null);
michael@0 617 this._destroyer.resolve(null);
michael@0 618 }.bind(this);
michael@0 619
michael@0 620 if (this.ui) {
michael@0 621 this.ui.destroy().then(onDestroy);
michael@0 622 }
michael@0 623 else {
michael@0 624 onDestroy();
michael@0 625 }
michael@0 626
michael@0 627 return this._destroyer.promise;
michael@0 628 },
michael@0 629 };
michael@0 630
michael@0 631
michael@0 632 /**
michael@0 633 * A BrowserConsole instance is an interactive console initialized *per target*
michael@0 634 * that displays console log data as well as provides an interactive terminal to
michael@0 635 * manipulate the target's document content.
michael@0 636 *
michael@0 637 * This object only wraps the iframe that holds the Browser Console UI. This is
michael@0 638 * meant to be an integration point between the Firefox UI and the Browser Console
michael@0 639 * UI and features.
michael@0 640 *
michael@0 641 * @constructor
michael@0 642 * @param object aTarget
michael@0 643 * The target that the browser console will connect to.
michael@0 644 * @param nsIDOMWindow aIframeWindow
michael@0 645 * The window where the browser console UI is already loaded.
michael@0 646 * @param nsIDOMWindow aChromeWindow
michael@0 647 * The window of the browser console owner.
michael@0 648 */
michael@0 649 function BrowserConsole()
michael@0 650 {
michael@0 651 WebConsole.apply(this, arguments);
michael@0 652 this._telemetry = new Telemetry();
michael@0 653 }
michael@0 654
michael@0 655 BrowserConsole.prototype = Heritage.extend(WebConsole.prototype,
michael@0 656 {
michael@0 657 _browserConsole: true,
michael@0 658 _bc_init: null,
michael@0 659 _bc_destroyer: null,
michael@0 660
michael@0 661 $init: WebConsole.prototype.init,
michael@0 662
michael@0 663 /**
michael@0 664 * Initialize the Browser Console instance.
michael@0 665 *
michael@0 666 * @return object
michael@0 667 * A promise for the initialization.
michael@0 668 */
michael@0 669 init: function BC_init()
michael@0 670 {
michael@0 671 if (this._bc_init) {
michael@0 672 return this._bc_init;
michael@0 673 }
michael@0 674
michael@0 675 this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
michael@0 676
michael@0 677 let window = this.iframeWindow;
michael@0 678
michael@0 679 // Make sure that the closing of the Browser Console window destroys this
michael@0 680 // instance.
michael@0 681 let onClose = () => {
michael@0 682 window.removeEventListener("unload", onClose);
michael@0 683 this.destroy();
michael@0 684 };
michael@0 685 window.addEventListener("unload", onClose);
michael@0 686
michael@0 687 // Make sure Ctrl-W closes the Browser Console window.
michael@0 688 window.document.getElementById("cmd_close").removeAttribute("disabled");
michael@0 689
michael@0 690 this._telemetry.toolOpened("browserconsole");
michael@0 691
michael@0 692 this._bc_init = this.$init();
michael@0 693 return this._bc_init;
michael@0 694 },
michael@0 695
michael@0 696 $destroy: WebConsole.prototype.destroy,
michael@0 697
michael@0 698 /**
michael@0 699 * Destroy the object.
michael@0 700 *
michael@0 701 * @return object
michael@0 702 * A promise object that is resolved once the Browser Console is closed.
michael@0 703 */
michael@0 704 destroy: function BC_destroy()
michael@0 705 {
michael@0 706 if (this._bc_destroyer) {
michael@0 707 return this._bc_destroyer.promise;
michael@0 708 }
michael@0 709
michael@0 710 this._telemetry.toolClosed("browserconsole");
michael@0 711
michael@0 712 this._bc_destroyer = promise.defer();
michael@0 713
michael@0 714 let chromeWindow = this.chromeWindow;
michael@0 715 this.$destroy().then(() =>
michael@0 716 this.target.client.close(() => {
michael@0 717 HUDService._browserConsoleID = null;
michael@0 718 chromeWindow.close();
michael@0 719 this._bc_destroyer.resolve(null);
michael@0 720 }));
michael@0 721
michael@0 722 return this._bc_destroyer.promise;
michael@0 723 },
michael@0 724 });
michael@0 725
michael@0 726 const HUDService = new HUD_SERVICE();
michael@0 727
michael@0 728 (() => {
michael@0 729 let methods = ["openWebConsole", "openBrowserConsole",
michael@0 730 "toggleBrowserConsole", "getOpenWebConsole",
michael@0 731 "getBrowserConsole", "getHudByWindow", "getHudReferenceById"];
michael@0 732 for (let method of methods) {
michael@0 733 exports[method] = HUDService[method].bind(HUDService);
michael@0 734 }
michael@0 735
michael@0 736 exports.consoles = HUDService.consoles;
michael@0 737 exports.lastFinishedRequest = HUDService.lastFinishedRequest;
michael@0 738 })();

mercurial