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 +})();