hudservice.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. const {Cc, Ci, Cu} = require("chrome");
  6. var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
  7. var { extend } = require("sdk/core/heritage");
  8. var {TargetFactory} = require("devtools/client/framework/target");
  9. var {Tools} = require("devtools/client/definitions");
  10. const { Task } = require("devtools/shared/task");
  11. var promise = require("promise");
  12. var Services = require("Services");
  13. loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
  14. loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
  15. loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
  16. loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
  17. loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
  18. loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
  19. loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
  20. const STRINGS_URI = "devtools/client/locales/webconsole.properties";
  21. var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
  22. const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
  23. // The preference prefix for all of the Browser Console filters.
  24. const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
  25. var gHudId = 0;
  26. // The HUD service
  27. function HUD_SERVICE()
  28. {
  29. this.consoles = new Map();
  30. this.lastFinishedRequest = { callback: null };
  31. }
  32. HUD_SERVICE.prototype =
  33. {
  34. _browserConsoleID: null,
  35. _browserConsoleDefer: null,
  36. /**
  37. * Keeps a reference for each Web Console / Browser Console that is created.
  38. * @type Map
  39. */
  40. consoles: null,
  41. _browerConsoleSessionState: false,
  42. storeBrowserConsoleSessionState() {
  43. this._browerConsoleSessionState = !!this.getBrowserConsole();
  44. },
  45. getBrowserConsoleSessionState() {
  46. return this._browerConsoleSessionState;
  47. },
  48. /**
  49. * Restore the Browser Console as provided by SessionStore.
  50. */
  51. restoreBrowserConsoleSession: function HS_restoreBrowserConsoleSession() {
  52. if (!HUDService.getBrowserConsole()) {
  53. HUDService.toggleBrowserConsole();
  54. }
  55. },
  56. /**
  57. * Assign a function to this property to listen for every request that
  58. * completes. Used by unit tests. The callback takes one argument: the HTTP
  59. * activity object as received from the remote Web Console.
  60. *
  61. * @type object
  62. * Includes a property named |callback|. Assign the function to the
  63. * |callback| property of this object.
  64. */
  65. lastFinishedRequest: null,
  66. /**
  67. * Get the current context, which is the main application window.
  68. *
  69. * @returns nsIDOMWindow
  70. */
  71. currentContext: function HS_currentContext() {
  72. return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  73. },
  74. /**
  75. * Open a Web Console for the given target.
  76. *
  77. * @see devtools/framework/target.js for details about targets.
  78. *
  79. * @param object aTarget
  80. * The target that the web console will connect to.
  81. * @param nsIDOMWindow aIframeWindow
  82. * The window where the web console UI is already loaded.
  83. * @param nsIDOMWindow aChromeWindow
  84. * The window of the web console owner.
  85. * @return object
  86. * A promise object for the opening of the new WebConsole instance.
  87. */
  88. openWebConsole:
  89. function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
  90. {
  91. let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
  92. this.consoles.set(hud.hudId, hud);
  93. return hud.init();
  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. * Returns the Web Console object associated to a content window.
  119. *
  120. * @param nsIDOMWindow aContentWindow
  121. * @returns object
  122. */
  123. getHudByWindow: function HS_getHudByWindow(aContentWindow)
  124. {
  125. for (let [hudId, hud] of this.consoles) {
  126. let target = hud.target;
  127. if (target && target.tab && target.window === aContentWindow) {
  128. return hud;
  129. }
  130. }
  131. return null;
  132. },
  133. /**
  134. * Returns the console instance for a given id.
  135. *
  136. * @param string aId
  137. * @returns Object
  138. */
  139. getHudReferenceById: function HS_getHudReferenceById(aId)
  140. {
  141. return this.consoles.get(aId);
  142. },
  143. /**
  144. * Find if there is a Web Console open for the current tab and return the
  145. * instance.
  146. * @return object|null
  147. * The WebConsole object or null if the active tab has no open Web
  148. * Console.
  149. */
  150. getOpenWebConsole: function HS_getOpenWebConsole()
  151. {
  152. let tab = this.currentContext().gBrowser.selectedTab;
  153. if (!tab || !TargetFactory.isKnownTab(tab)) {
  154. return null;
  155. }
  156. let target = TargetFactory.forTab(tab);
  157. let toolbox = gDevTools.getToolbox(target);
  158. let panel = toolbox ? toolbox.getPanel("webconsole") : null;
  159. return panel ? panel.hud : null;
  160. },
  161. /**
  162. * Toggle the Browser Console.
  163. */
  164. toggleBrowserConsole: function HS_toggleBrowserConsole()
  165. {
  166. if (this._browserConsoleID) {
  167. let hud = this.getHudReferenceById(this._browserConsoleID);
  168. return hud.destroy();
  169. }
  170. if (this._browserConsoleDefer) {
  171. return this._browserConsoleDefer.promise;
  172. }
  173. this._browserConsoleDefer = promise.defer();
  174. function connect()
  175. {
  176. let deferred = promise.defer();
  177. if (!DebuggerServer.initialized) {
  178. DebuggerServer.init();
  179. DebuggerServer.addBrowserActors();
  180. }
  181. DebuggerServer.allowChromeProcess = true;
  182. let client = new DebuggerClient(DebuggerServer.connectPipe());
  183. return client.connect()
  184. .then(() => client.getProcess())
  185. .then(aResponse => {
  186. // Set chrome:false in order to attach to the target
  187. // (i.e. send an `attach` request to the chrome actor)
  188. return { form: aResponse.form, client: client, chrome: false };
  189. });
  190. }
  191. let target;
  192. function getTarget(aConnection)
  193. {
  194. return TargetFactory.forRemoteTab(aConnection);
  195. }
  196. function openWindow(aTarget)
  197. {
  198. target = aTarget;
  199. let deferred = promise.defer();
  200. let win = Services.ww.openWindow(null, Tools.webConsole.url, "_blank",
  201. BROWSER_CONSOLE_WINDOW_FEATURES, null);
  202. win.addEventListener("DOMContentLoaded", function onLoad() {
  203. win.removeEventListener("DOMContentLoaded", onLoad);
  204. // Set the correct Browser Console title.
  205. let root = win.document.documentElement;
  206. root.setAttribute("title", root.getAttribute("browserConsoleTitle"));
  207. deferred.resolve(win);
  208. });
  209. return deferred.promise;
  210. }
  211. connect().then(getTarget).then(openWindow).then((aWindow) => {
  212. return this.openBrowserConsole(target, aWindow, aWindow)
  213. .then((aBrowserConsole) => {
  214. this._browserConsoleDefer.resolve(aBrowserConsole);
  215. this._browserConsoleDefer = null;
  216. });
  217. }, console.error.bind(console));
  218. return this._browserConsoleDefer.promise;
  219. },
  220. /**
  221. * Opens or focuses the Browser Console.
  222. */
  223. openBrowserConsoleOrFocus: function HS_openBrowserConsoleOrFocus()
  224. {
  225. let hud = this.getBrowserConsole();
  226. if (hud) {
  227. hud.iframeWindow.focus();
  228. return promise.resolve(hud);
  229. }
  230. else {
  231. return this.toggleBrowserConsole();
  232. }
  233. },
  234. /**
  235. * Get the Browser Console instance, if open.
  236. *
  237. * @return object|null
  238. * A BrowserConsole instance or null if the Browser Console is not
  239. * open.
  240. */
  241. getBrowserConsole: function HS_getBrowserConsole()
  242. {
  243. return this.getHudReferenceById(this._browserConsoleID);
  244. },
  245. };
  246. /**
  247. * A WebConsole instance is an interactive console initialized *per target*
  248. * that displays console log data as well as provides an interactive terminal to
  249. * manipulate the target's document content.
  250. *
  251. * This object only wraps the iframe that holds the Web Console UI. This is
  252. * meant to be an integration point between the Firefox UI and the Web Console
  253. * UI and features.
  254. *
  255. * @constructor
  256. * @param object aTarget
  257. * The target that the web console will connect to.
  258. * @param nsIDOMWindow aIframeWindow
  259. * The window where the web console UI is already loaded.
  260. * @param nsIDOMWindow aChromeWindow
  261. * The window of the web console owner.
  262. */
  263. function WebConsole(aTarget, aIframeWindow, aChromeWindow)
  264. {
  265. this.iframeWindow = aIframeWindow;
  266. this.chromeWindow = aChromeWindow;
  267. this.hudId = "hud_" + ++gHudId;
  268. this.target = aTarget;
  269. this.browserWindow = this.chromeWindow.top;
  270. let element = this.browserWindow.document.documentElement;
  271. if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
  272. this.browserWindow = HUDService.currentContext();
  273. }
  274. this.ui = new WebConsoleFrame(this);
  275. }
  276. WebConsole.prototype = {
  277. iframeWindow: null,
  278. chromeWindow: null,
  279. browserWindow: null,
  280. hudId: null,
  281. target: null,
  282. ui: null,
  283. _browserConsole: false,
  284. _destroyer: null,
  285. /**
  286. * Getter for a function to to listen for every request that completes. Used
  287. * by unit tests. The callback takes one argument: the HTTP activity object as
  288. * received from the remote Web Console.
  289. *
  290. * @type function
  291. */
  292. get lastFinishedRequestCallback()
  293. {
  294. return HUDService.lastFinishedRequest.callback;
  295. },
  296. /**
  297. * Getter for the window that can provide various utilities that the web
  298. * console makes use of, like opening links, managing popups, etc. In
  299. * most cases, this will be |this.browserWindow|, but in some uses (such as
  300. * the Browser Toolbox), there is no browser window, so an alternative window
  301. * hosts the utilities there.
  302. * @type nsIDOMWindow
  303. */
  304. get chromeUtilsWindow()
  305. {
  306. if (this.browserWindow) {
  307. return this.browserWindow;
  308. }
  309. return this.chromeWindow.top;
  310. },
  311. /**
  312. * Getter for the xul:popupset that holds any popups we open.
  313. * @type nsIDOMElement
  314. */
  315. get mainPopupSet()
  316. {
  317. return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
  318. },
  319. /**
  320. * Getter for the output element that holds messages we display.
  321. * @type nsIDOMElement
  322. */
  323. get outputNode()
  324. {
  325. return this.ui ? this.ui.outputNode : null;
  326. },
  327. get gViewSourceUtils()
  328. {
  329. return this.chromeUtilsWindow.gViewSourceUtils;
  330. },
  331. /**
  332. * Initialize the Web Console instance.
  333. *
  334. * @return object
  335. * A promise for the initialization.
  336. */
  337. init: function WC_init()
  338. {
  339. return this.ui.init().then(() => this);
  340. },
  341. /**
  342. * Retrieve the Web Console panel title.
  343. *
  344. * @return string
  345. * The Web Console panel title.
  346. */
  347. getPanelTitle: function WC_getPanelTitle()
  348. {
  349. let url = this.ui ? this.ui.contentLocation : "";
  350. return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
  351. },
  352. /**
  353. * The JSTerm object that manages the console's input.
  354. * @see webconsole.js::JSTerm
  355. * @type object
  356. */
  357. get jsterm()
  358. {
  359. return this.ui ? this.ui.jsterm : null;
  360. },
  361. /**
  362. * The clear output button handler.
  363. * @private
  364. */
  365. _onClearButton: function WC__onClearButton()
  366. {
  367. if (this.target.isLocalTab) {
  368. this.browserWindow.DeveloperToolbar.resetErrorsCount(this.target.tab);
  369. }
  370. },
  371. /**
  372. * Alias for the WebConsoleFrame.setFilterState() method.
  373. * @see webconsole.js::WebConsoleFrame.setFilterState()
  374. */
  375. setFilterState: function WC_setFilterState()
  376. {
  377. this.ui && this.ui.setFilterState.apply(this.ui, arguments);
  378. },
  379. /**
  380. * Open a link in a new tab.
  381. *
  382. * @param string aLink
  383. * The URL you want to open in a new tab.
  384. */
  385. openLink: function WC_openLink(aLink)
  386. {
  387. this.chromeUtilsWindow.openUILinkIn(aLink, "tab");
  388. },
  389. /**
  390. * Open a link in Firefox's view source.
  391. *
  392. * @param string aSourceURL
  393. * The URL of the file.
  394. * @param integer aSourceLine
  395. * The line number which should be highlighted.
  396. */
  397. viewSource: function WC_viewSource(aSourceURL, aSourceLine) {
  398. // Attempt to access view source via a browser first, which may display it in
  399. // a tab, if enabled.
  400. let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
  401. if (browserWin && browserWin.BrowserViewSourceOfDocument) {
  402. return browserWin.BrowserViewSourceOfDocument({
  403. URL: aSourceURL,
  404. lineNumber: aSourceLine
  405. });
  406. }
  407. this.gViewSourceUtils.viewSource(aSourceURL, null, this.iframeWindow.document, aSourceLine || 0);
  408. },
  409. /**
  410. * Tries to open a Stylesheet file related to the web page for the web console
  411. * instance in the Style Editor. If the file is not found, it is opened in
  412. * source view instead.
  413. *
  414. * Manually handle the case where toolbox does not exist (Browser Console).
  415. *
  416. * @param string aSourceURL
  417. * The URL of the file.
  418. * @param integer aSourceLine
  419. * The line number which you want to place the caret.
  420. */
  421. viewSourceInStyleEditor: function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine) {
  422. let toolbox = gDevTools.getToolbox(this.target);
  423. if (!toolbox) {
  424. this.viewSource(aSourceURL, aSourceLine);
  425. return;
  426. }
  427. toolbox.viewSourceInStyleEditor(aSourceURL, aSourceLine);
  428. },
  429. /**
  430. * Tries to open a JavaScript file related to the web page for the web console
  431. * instance in the Script Debugger. If the file is not found, it is opened in
  432. * source view instead.
  433. *
  434. * Manually handle the case where toolbox does not exist (Browser Console).
  435. *
  436. * @param string aSourceURL
  437. * The URL of the file.
  438. * @param integer aSourceLine
  439. * The line number which you want to place the caret.
  440. */
  441. viewSourceInDebugger: function WC_viewSourceInDebugger(aSourceURL, aSourceLine) {
  442. let toolbox = gDevTools.getToolbox(this.target);
  443. if (!toolbox) {
  444. this.viewSource(aSourceURL, aSourceLine);
  445. return;
  446. }
  447. toolbox.viewSourceInDebugger(aSourceURL, aSourceLine).then(() => {
  448. this.ui.emit("source-in-debugger-opened");
  449. });
  450. },
  451. /**
  452. * Tries to open a JavaScript file related to the web page for the web console
  453. * instance in the corresponding Scratchpad.
  454. *
  455. * @param string aSourceURL
  456. * The URL of the file which corresponds to a Scratchpad id.
  457. */
  458. viewSourceInScratchpad: function WC_viewSourceInScratchpad(aSourceURL, aSourceLine) {
  459. viewSource.viewSourceInScratchpad(aSourceURL, aSourceLine);
  460. },
  461. /**
  462. * Retrieve information about the JavaScript debugger's stackframes list. This
  463. * is used to allow the Web Console to evaluate code in the selected
  464. * stackframe.
  465. *
  466. * @return object|null
  467. * An object which holds:
  468. * - frames: the active ThreadClient.cachedFrames array.
  469. * - selected: depth/index of the selected stackframe in the debugger
  470. * UI.
  471. * If the debugger is not open or if it's not paused, then |null| is
  472. * returned.
  473. */
  474. getDebuggerFrames: function WC_getDebuggerFrames()
  475. {
  476. let toolbox = gDevTools.getToolbox(this.target);
  477. if (!toolbox) {
  478. return null;
  479. }
  480. let panel = toolbox.getPanel("jsdebugger");
  481. if (!panel) {
  482. return null;
  483. }
  484. return panel.getFrames();
  485. },
  486. /**
  487. * Retrieves the current selection from the Inspector, if such a selection
  488. * exists. This is used to pass the ID of the selected actor to the Web
  489. * Console server for the $0 helper.
  490. *
  491. * @return object|null
  492. * A Selection referring to the currently selected node in the
  493. * Inspector.
  494. * If the inspector was never opened, or no node was ever selected,
  495. * then |null| is returned.
  496. */
  497. getInspectorSelection: function WC_getInspectorSelection()
  498. {
  499. let toolbox = gDevTools.getToolbox(this.target);
  500. if (!toolbox) {
  501. return null;
  502. }
  503. let panel = toolbox.getPanel("inspector");
  504. if (!panel || !panel.selection) {
  505. return null;
  506. }
  507. return panel.selection;
  508. },
  509. /**
  510. * Destroy the object. Call this method to avoid memory leaks when the Web
  511. * Console is closed.
  512. *
  513. * @return object
  514. * A promise object that is resolved once the Web Console is closed.
  515. */
  516. destroy: function WC_destroy()
  517. {
  518. if (this._destroyer) {
  519. return this._destroyer.promise;
  520. }
  521. HUDService.consoles.delete(this.hudId);
  522. this._destroyer = promise.defer();
  523. // The document may already be removed
  524. if (this.chromeUtilsWindow && this.mainPopupSet) {
  525. let popupset = this.mainPopupSet;
  526. let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
  527. for (let panel of panels) {
  528. panel.hidePopup();
  529. }
  530. }
  531. let onDestroy = Task.async(function* () {
  532. if (!this._browserConsole) {
  533. try {
  534. yield this.target.activeTab.focus();
  535. }
  536. catch (ex) {
  537. // Tab focus can fail if the tab or target is closed.
  538. }
  539. }
  540. let id = WebConsoleUtils.supportsString(this.hudId);
  541. Services.obs.notifyObservers(id, "web-console-destroyed", null);
  542. this._destroyer.resolve(null);
  543. }.bind(this));
  544. if (this.ui) {
  545. this.ui.destroy().then(onDestroy);
  546. }
  547. else {
  548. onDestroy();
  549. }
  550. return this._destroyer.promise;
  551. },
  552. };
  553. /**
  554. * A BrowserConsole instance is an interactive console initialized *per target*
  555. * that displays console log data as well as provides an interactive terminal to
  556. * manipulate the target's document content.
  557. *
  558. * This object only wraps the iframe that holds the Browser Console UI. This is
  559. * meant to be an integration point between the Firefox UI and the Browser Console
  560. * UI and features.
  561. *
  562. * @constructor
  563. * @param object aTarget
  564. * The target that the browser console will connect to.
  565. * @param nsIDOMWindow aIframeWindow
  566. * The window where the browser console UI is already loaded.
  567. * @param nsIDOMWindow aChromeWindow
  568. * The window of the browser console owner.
  569. */
  570. function BrowserConsole()
  571. {
  572. WebConsole.apply(this, arguments);
  573. this._telemetry = new Telemetry();
  574. }
  575. BrowserConsole.prototype = extend(WebConsole.prototype, {
  576. _browserConsole: true,
  577. _bc_init: null,
  578. _bc_destroyer: null,
  579. $init: WebConsole.prototype.init,
  580. /**
  581. * Initialize the Browser Console instance.
  582. *
  583. * @return object
  584. * A promise for the initialization.
  585. */
  586. init: function BC_init()
  587. {
  588. if (this._bc_init) {
  589. return this._bc_init;
  590. }
  591. // Only add the shutdown observer if we've opened a Browser Console window.
  592. ShutdownObserver.init();
  593. this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
  594. let window = this.iframeWindow;
  595. // Make sure that the closing of the Browser Console window destroys this
  596. // instance.
  597. let onClose = () => {
  598. window.removeEventListener("unload", onClose);
  599. window.removeEventListener("focus", onFocus);
  600. this.destroy();
  601. };
  602. window.addEventListener("unload", onClose);
  603. this._telemetry.toolOpened("browserconsole");
  604. // Create an onFocus handler just to display the dev edition promo.
  605. // This is to prevent race conditions in some environments.
  606. // Hook to display promotional Developer Edition doorhanger. Only displayed once.
  607. let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
  608. window.addEventListener("focus", onFocus);
  609. this._bc_init = this.$init();
  610. return this._bc_init;
  611. },
  612. $destroy: WebConsole.prototype.destroy,
  613. /**
  614. * Destroy the object.
  615. *
  616. * @return object
  617. * A promise object that is resolved once the Browser Console is closed.
  618. */
  619. destroy: function BC_destroy()
  620. {
  621. if (this._bc_destroyer) {
  622. return this._bc_destroyer.promise;
  623. }
  624. this._telemetry.toolClosed("browserconsole");
  625. this._bc_destroyer = promise.defer();
  626. let chromeWindow = this.chromeWindow;
  627. this.$destroy().then(() =>
  628. this.target.client.close().then(() => {
  629. HUDService._browserConsoleID = null;
  630. chromeWindow.close();
  631. this._bc_destroyer.resolve(null);
  632. }));
  633. return this._bc_destroyer.promise;
  634. },
  635. });
  636. const HUDService = new HUD_SERVICE();
  637. exports.HUDService = HUDService;
  638. /**
  639. * The ShutdownObserver listens for app shutdown and saves the current state
  640. * of the Browser Console for session restore.
  641. */
  642. var ShutdownObserver = {
  643. _initialized: false,
  644. init() {
  645. if (this._initialized) {
  646. return;
  647. }
  648. Services.obs.addObserver(this, "quit-application-granted", false);
  649. this._initialized = true;
  650. },
  651. observe(message, topic) {
  652. if (topic == "quit-application-granted") {
  653. HUDService.storeBrowserConsoleSessionState();
  654. this.uninit();
  655. }
  656. },
  657. uninit() {
  658. Services.obs.removeObserver(this, "quit-application-granted");
  659. }
  660. };