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