123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const {Cc, Ci, Cu} = require("chrome");
- var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
- var { extend } = require("sdk/core/heritage");
- var {TargetFactory} = require("devtools/client/framework/target");
- var {Tools} = require("devtools/client/definitions");
- const { Task } = require("devtools/shared/task");
- var promise = require("promise");
- var Services = require("Services");
- loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
- loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
- loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
- loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
- loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
- loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
- loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
- const STRINGS_URI = "devtools/client/locales/webconsole.properties";
- var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
- const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
- // The preference prefix for all of the Browser Console filters.
- const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
- var gHudId = 0;
- // The HUD service
- function HUD_SERVICE()
- {
- this.consoles = new Map();
- this.lastFinishedRequest = { callback: null };
- }
- HUD_SERVICE.prototype =
- {
- _browserConsoleID: null,
- _browserConsoleDefer: null,
- /**
- * Keeps a reference for each Web Console / Browser Console that is created.
- * @type Map
- */
- consoles: null,
- _browerConsoleSessionState: false,
- storeBrowserConsoleSessionState() {
- this._browerConsoleSessionState = !!this.getBrowserConsole();
- },
- getBrowserConsoleSessionState() {
- return this._browerConsoleSessionState;
- },
- /**
- * Restore the Browser Console as provided by SessionStore.
- */
- restoreBrowserConsoleSession: function HS_restoreBrowserConsoleSession() {
- if (!HUDService.getBrowserConsole()) {
- HUDService.toggleBrowserConsole();
- }
- },
- /**
- * Assign a function to this property to listen for every request that
- * completes. Used by unit tests. The callback takes one argument: the HTTP
- * activity object as received from the remote Web Console.
- *
- * @type object
- * Includes a property named |callback|. Assign the function to the
- * |callback| property of this object.
- */
- lastFinishedRequest: null,
- /**
- * Get the current context, which is the main application window.
- *
- * @returns nsIDOMWindow
- */
- currentContext: function HS_currentContext() {
- return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
- },
- /**
- * Open a Web Console for the given target.
- *
- * @see devtools/framework/target.js for details about targets.
- *
- * @param object aTarget
- * The target that the web console will connect to.
- * @param nsIDOMWindow aIframeWindow
- * The window where the web console UI is already loaded.
- * @param nsIDOMWindow aChromeWindow
- * The window of the web console owner.
- * @return object
- * A promise object for the opening of the new WebConsole instance.
- */
- openWebConsole:
- function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
- {
- let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
- this.consoles.set(hud.hudId, hud);
- return hud.init();
- },
- /**
- * Open a Browser Console for the given target.
- *
- * @see devtools/framework/target.js for details about targets.
- *
- * @param object aTarget
- * The target that the browser console will connect to.
- * @param nsIDOMWindow aIframeWindow
- * The window where the browser console UI is already loaded.
- * @param nsIDOMWindow aChromeWindow
- * The window of the browser console owner.
- * @return object
- * A promise object for the opening of the new BrowserConsole instance.
- */
- openBrowserConsole:
- function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow)
- {
- let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow);
- this._browserConsoleID = hud.hudId;
- this.consoles.set(hud.hudId, hud);
- return hud.init();
- },
- /**
- * Returns the Web Console object associated to a content window.
- *
- * @param nsIDOMWindow aContentWindow
- * @returns object
- */
- getHudByWindow: function HS_getHudByWindow(aContentWindow)
- {
- for (let [hudId, hud] of this.consoles) {
- let target = hud.target;
- if (target && target.tab && target.window === aContentWindow) {
- return hud;
- }
- }
- return null;
- },
- /**
- * Returns the console instance for a given id.
- *
- * @param string aId
- * @returns Object
- */
- getHudReferenceById: function HS_getHudReferenceById(aId)
- {
- return this.consoles.get(aId);
- },
- /**
- * Find if there is a Web Console open for the current tab and return the
- * instance.
- * @return object|null
- * The WebConsole object or null if the active tab has no open Web
- * Console.
- */
- getOpenWebConsole: function HS_getOpenWebConsole()
- {
- let tab = this.currentContext().gBrowser.selectedTab;
- if (!tab || !TargetFactory.isKnownTab(tab)) {
- return null;
- }
- let target = TargetFactory.forTab(tab);
- let toolbox = gDevTools.getToolbox(target);
- let panel = toolbox ? toolbox.getPanel("webconsole") : null;
- return panel ? panel.hud : null;
- },
- /**
- * Toggle the Browser Console.
- */
- toggleBrowserConsole: function HS_toggleBrowserConsole()
- {
- if (this._browserConsoleID) {
- let hud = this.getHudReferenceById(this._browserConsoleID);
- return hud.destroy();
- }
- if (this._browserConsoleDefer) {
- return this._browserConsoleDefer.promise;
- }
- this._browserConsoleDefer = promise.defer();
- function connect()
- {
- let deferred = promise.defer();
- if (!DebuggerServer.initialized) {
- DebuggerServer.init();
- DebuggerServer.addBrowserActors();
- }
- DebuggerServer.allowChromeProcess = true;
- let client = new DebuggerClient(DebuggerServer.connectPipe());
- return client.connect()
- .then(() => client.getProcess())
- .then(aResponse => {
- // Set chrome:false in order to attach to the target
- // (i.e. send an `attach` request to the chrome actor)
- return { form: aResponse.form, client: client, chrome: false };
- });
- }
- let target;
- function getTarget(aConnection)
- {
- return TargetFactory.forRemoteTab(aConnection);
- }
- function openWindow(aTarget)
- {
- target = aTarget;
- let deferred = promise.defer();
- let win = Services.ww.openWindow(null, Tools.webConsole.url, "_blank",
- BROWSER_CONSOLE_WINDOW_FEATURES, null);
- win.addEventListener("DOMContentLoaded", function onLoad() {
- win.removeEventListener("DOMContentLoaded", onLoad);
- // Set the correct Browser Console title.
- let root = win.document.documentElement;
- root.setAttribute("title", root.getAttribute("browserConsoleTitle"));
- deferred.resolve(win);
- });
- return deferred.promise;
- }
- connect().then(getTarget).then(openWindow).then((aWindow) => {
- return this.openBrowserConsole(target, aWindow, aWindow)
- .then((aBrowserConsole) => {
- this._browserConsoleDefer.resolve(aBrowserConsole);
- this._browserConsoleDefer = null;
- });
- }, console.error.bind(console));
- return this._browserConsoleDefer.promise;
- },
- /**
- * Opens or focuses the Browser Console.
- */
- openBrowserConsoleOrFocus: function HS_openBrowserConsoleOrFocus()
- {
- let hud = this.getBrowserConsole();
- if (hud) {
- hud.iframeWindow.focus();
- return promise.resolve(hud);
- }
- else {
- return this.toggleBrowserConsole();
- }
- },
- /**
- * Get the Browser Console instance, if open.
- *
- * @return object|null
- * A BrowserConsole instance or null if the Browser Console is not
- * open.
- */
- getBrowserConsole: function HS_getBrowserConsole()
- {
- return this.getHudReferenceById(this._browserConsoleID);
- },
- };
- /**
- * A WebConsole instance is an interactive console initialized *per target*
- * that displays console log data as well as provides an interactive terminal to
- * manipulate the target's document content.
- *
- * This object only wraps the iframe that holds the Web Console UI. This is
- * meant to be an integration point between the Firefox UI and the Web Console
- * UI and features.
- *
- * @constructor
- * @param object aTarget
- * The target that the web console will connect to.
- * @param nsIDOMWindow aIframeWindow
- * The window where the web console UI is already loaded.
- * @param nsIDOMWindow aChromeWindow
- * The window of the web console owner.
- */
- function WebConsole(aTarget, aIframeWindow, aChromeWindow)
- {
- this.iframeWindow = aIframeWindow;
- this.chromeWindow = aChromeWindow;
- this.hudId = "hud_" + ++gHudId;
- this.target = aTarget;
- this.browserWindow = this.chromeWindow.top;
- let element = this.browserWindow.document.documentElement;
- if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
- this.browserWindow = HUDService.currentContext();
- }
- this.ui = new WebConsoleFrame(this);
- }
- WebConsole.prototype = {
- iframeWindow: null,
- chromeWindow: null,
- browserWindow: null,
- hudId: null,
- target: null,
- ui: null,
- _browserConsole: false,
- _destroyer: null,
- /**
- * Getter for a function to to listen for every request that completes. Used
- * by unit tests. The callback takes one argument: the HTTP activity object as
- * received from the remote Web Console.
- *
- * @type function
- */
- get lastFinishedRequestCallback()
- {
- return HUDService.lastFinishedRequest.callback;
- },
- /**
- * Getter for the window that can provide various utilities that the web
- * console makes use of, like opening links, managing popups, etc. In
- * most cases, this will be |this.browserWindow|, but in some uses (such as
- * the Browser Toolbox), there is no browser window, so an alternative window
- * hosts the utilities there.
- * @type nsIDOMWindow
- */
- get chromeUtilsWindow()
- {
- if (this.browserWindow) {
- return this.browserWindow;
- }
- return this.chromeWindow.top;
- },
- /**
- * Getter for the xul:popupset that holds any popups we open.
- * @type nsIDOMElement
- */
- get mainPopupSet()
- {
- return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
- },
- /**
- * Getter for the output element that holds messages we display.
- * @type nsIDOMElement
- */
- get outputNode()
- {
- return this.ui ? this.ui.outputNode : null;
- },
- get gViewSourceUtils()
- {
- return this.chromeUtilsWindow.gViewSourceUtils;
- },
- /**
- * Initialize the Web Console instance.
- *
- * @return object
- * A promise for the initialization.
- */
- init: function WC_init()
- {
- return this.ui.init().then(() => this);
- },
- /**
- * Retrieve the Web Console panel title.
- *
- * @return string
- * The Web Console panel title.
- */
- getPanelTitle: function WC_getPanelTitle()
- {
- let url = this.ui ? this.ui.contentLocation : "";
- return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
- },
- /**
- * The JSTerm object that manages the console's input.
- * @see webconsole.js::JSTerm
- * @type object
- */
- get jsterm()
- {
- return this.ui ? this.ui.jsterm : null;
- },
- /**
- * The clear output button handler.
- * @private
- */
- _onClearButton: function WC__onClearButton()
- {
- if (this.target.isLocalTab) {
- this.browserWindow.DeveloperToolbar.resetErrorsCount(this.target.tab);
- }
- },
- /**
- * Alias for the WebConsoleFrame.setFilterState() method.
- * @see webconsole.js::WebConsoleFrame.setFilterState()
- */
- setFilterState: function WC_setFilterState()
- {
- this.ui && this.ui.setFilterState.apply(this.ui, arguments);
- },
- /**
- * Open a link in a new tab.
- *
- * @param string aLink
- * The URL you want to open in a new tab.
- */
- openLink: function WC_openLink(aLink)
- {
- this.chromeUtilsWindow.openUILinkIn(aLink, "tab");
- },
- /**
- * Open a link in Firefox's view source.
- *
- * @param string aSourceURL
- * The URL of the file.
- * @param integer aSourceLine
- * The line number which should be highlighted.
- */
- viewSource: function WC_viewSource(aSourceURL, aSourceLine) {
- // Attempt to access view source via a browser first, which may display it in
- // a tab, if enabled.
- let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
- if (browserWin && browserWin.BrowserViewSourceOfDocument) {
- return browserWin.BrowserViewSourceOfDocument({
- URL: aSourceURL,
- lineNumber: aSourceLine
- });
- }
- this.gViewSourceUtils.viewSource(aSourceURL, null, this.iframeWindow.document, aSourceLine || 0);
- },
- /**
- * Tries to open a Stylesheet file related to the web page for the web console
- * instance in the Style Editor. If the file is not found, it is opened in
- * source view instead.
- *
- * Manually handle the case where toolbox does not exist (Browser Console).
- *
- * @param string aSourceURL
- * The URL of the file.
- * @param integer aSourceLine
- * The line number which you want to place the caret.
- */
- viewSourceInStyleEditor: function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine) {
- let toolbox = gDevTools.getToolbox(this.target);
- if (!toolbox) {
- this.viewSource(aSourceURL, aSourceLine);
- return;
- }
- toolbox.viewSourceInStyleEditor(aSourceURL, aSourceLine);
- },
- /**
- * Tries to open a JavaScript file related to the web page for the web console
- * instance in the Script Debugger. If the file is not found, it is opened in
- * source view instead.
- *
- * Manually handle the case where toolbox does not exist (Browser Console).
- *
- * @param string aSourceURL
- * The URL of the file.
- * @param integer aSourceLine
- * The line number which you want to place the caret.
- */
- viewSourceInDebugger: function WC_viewSourceInDebugger(aSourceURL, aSourceLine) {
- let toolbox = gDevTools.getToolbox(this.target);
- if (!toolbox) {
- this.viewSource(aSourceURL, aSourceLine);
- return;
- }
- toolbox.viewSourceInDebugger(aSourceURL, aSourceLine).then(() => {
- this.ui.emit("source-in-debugger-opened");
- });
- },
- /**
- * Tries to open a JavaScript file related to the web page for the web console
- * instance in the corresponding Scratchpad.
- *
- * @param string aSourceURL
- * The URL of the file which corresponds to a Scratchpad id.
- */
- viewSourceInScratchpad: function WC_viewSourceInScratchpad(aSourceURL, aSourceLine) {
- viewSource.viewSourceInScratchpad(aSourceURL, aSourceLine);
- },
- /**
- * Retrieve information about the JavaScript debugger's stackframes list. This
- * is used to allow the Web Console to evaluate code in the selected
- * stackframe.
- *
- * @return object|null
- * An object which holds:
- * - frames: the active ThreadClient.cachedFrames array.
- * - selected: depth/index of the selected stackframe in the debugger
- * UI.
- * If the debugger is not open or if it's not paused, then |null| is
- * returned.
- */
- getDebuggerFrames: function WC_getDebuggerFrames()
- {
- let toolbox = gDevTools.getToolbox(this.target);
- if (!toolbox) {
- return null;
- }
- let panel = toolbox.getPanel("jsdebugger");
- if (!panel) {
- return null;
- }
- return panel.getFrames();
- },
- /**
- * Retrieves the current selection from the Inspector, if such a selection
- * exists. This is used to pass the ID of the selected actor to the Web
- * Console server for the $0 helper.
- *
- * @return object|null
- * A Selection referring to the currently selected node in the
- * Inspector.
- * If the inspector was never opened, or no node was ever selected,
- * then |null| is returned.
- */
- getInspectorSelection: function WC_getInspectorSelection()
- {
- let toolbox = gDevTools.getToolbox(this.target);
- if (!toolbox) {
- return null;
- }
- let panel = toolbox.getPanel("inspector");
- if (!panel || !panel.selection) {
- return null;
- }
- return panel.selection;
- },
- /**
- * Destroy the object. Call this method to avoid memory leaks when the Web
- * Console is closed.
- *
- * @return object
- * A promise object that is resolved once the Web Console is closed.
- */
- destroy: function WC_destroy()
- {
- if (this._destroyer) {
- return this._destroyer.promise;
- }
- HUDService.consoles.delete(this.hudId);
- this._destroyer = promise.defer();
- // The document may already be removed
- if (this.chromeUtilsWindow && this.mainPopupSet) {
- let popupset = this.mainPopupSet;
- let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
- for (let panel of panels) {
- panel.hidePopup();
- }
- }
- let onDestroy = Task.async(function* () {
- if (!this._browserConsole) {
- try {
- yield this.target.activeTab.focus();
- }
- catch (ex) {
- // Tab focus can fail if the tab or target is closed.
- }
- }
- let id = WebConsoleUtils.supportsString(this.hudId);
- Services.obs.notifyObservers(id, "web-console-destroyed", null);
- this._destroyer.resolve(null);
- }.bind(this));
- if (this.ui) {
- this.ui.destroy().then(onDestroy);
- }
- else {
- onDestroy();
- }
- return this._destroyer.promise;
- },
- };
- /**
- * A BrowserConsole instance is an interactive console initialized *per target*
- * that displays console log data as well as provides an interactive terminal to
- * manipulate the target's document content.
- *
- * This object only wraps the iframe that holds the Browser Console UI. This is
- * meant to be an integration point between the Firefox UI and the Browser Console
- * UI and features.
- *
- * @constructor
- * @param object aTarget
- * The target that the browser console will connect to.
- * @param nsIDOMWindow aIframeWindow
- * The window where the browser console UI is already loaded.
- * @param nsIDOMWindow aChromeWindow
- * The window of the browser console owner.
- */
- function BrowserConsole()
- {
- WebConsole.apply(this, arguments);
- this._telemetry = new Telemetry();
- }
- BrowserConsole.prototype = extend(WebConsole.prototype, {
- _browserConsole: true,
- _bc_init: null,
- _bc_destroyer: null,
- $init: WebConsole.prototype.init,
- /**
- * Initialize the Browser Console instance.
- *
- * @return object
- * A promise for the initialization.
- */
- init: function BC_init()
- {
- if (this._bc_init) {
- return this._bc_init;
- }
- // Only add the shutdown observer if we've opened a Browser Console window.
- ShutdownObserver.init();
- this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
- let window = this.iframeWindow;
- // Make sure that the closing of the Browser Console window destroys this
- // instance.
- let onClose = () => {
- window.removeEventListener("unload", onClose);
- window.removeEventListener("focus", onFocus);
- this.destroy();
- };
- window.addEventListener("unload", onClose);
- this._telemetry.toolOpened("browserconsole");
- // Create an onFocus handler just to display the dev edition promo.
- // This is to prevent race conditions in some environments.
- // Hook to display promotional Developer Edition doorhanger. Only displayed once.
- let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
- window.addEventListener("focus", onFocus);
- this._bc_init = this.$init();
- return this._bc_init;
- },
- $destroy: WebConsole.prototype.destroy,
- /**
- * Destroy the object.
- *
- * @return object
- * A promise object that is resolved once the Browser Console is closed.
- */
- destroy: function BC_destroy()
- {
- if (this._bc_destroyer) {
- return this._bc_destroyer.promise;
- }
- this._telemetry.toolClosed("browserconsole");
- this._bc_destroyer = promise.defer();
- let chromeWindow = this.chromeWindow;
- this.$destroy().then(() =>
- this.target.client.close().then(() => {
- HUDService._browserConsoleID = null;
- chromeWindow.close();
- this._bc_destroyer.resolve(null);
- }));
- return this._bc_destroyer.promise;
- },
- });
- const HUDService = new HUD_SERVICE();
- exports.HUDService = HUDService;
- /**
- * The ShutdownObserver listens for app shutdown and saves the current state
- * of the Browser Console for session restore.
- */
- var ShutdownObserver = {
- _initialized: false,
- init() {
- if (this._initialized) {
- return;
- }
- Services.obs.addObserver(this, "quit-application-granted", false);
- this._initialized = true;
- },
- observe(message, topic) {
- if (topic == "quit-application-granted") {
- HUDService.storeBrowserConsoleSessionState();
- this.uninit();
- }
- },
- uninit() {
- Services.obs.removeObserver(this, "quit-application-granted");
- }
- };
|