2017-04-26 18:10:04 +03:00
|
|
|
/* 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 { Utils: WebConsoleUtils } = require("devtools/client/webconsole/utils");
|
2018-02-23 11:10:36 +03:00
|
|
|
const EventEmitter = require("devtools/shared/event-emitter");
|
2017-04-26 18:10:04 +03:00
|
|
|
const Services = require("Services");
|
|
|
|
const {
|
|
|
|
WebConsoleConnectionProxy,
|
|
|
|
} = require("devtools/client/webconsole/webconsole-connection-proxy");
|
2017-06-09 19:42:34 +03:00
|
|
|
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
|
2018-04-06 16:03:20 +03:00
|
|
|
const { l10n } = require("devtools/client/webconsole/utils/messages");
|
2018-03-20 00:18:20 +03:00
|
|
|
|
2019-07-18 14:40:38 +03:00
|
|
|
var ChromeUtils = require("ChromeUtils");
|
|
|
|
const { BrowserLoader } = ChromeUtils.import(
|
|
|
|
"resource://devtools/client/shared/browser-loader.js"
|
|
|
|
);
|
|
|
|
|
2018-03-20 00:18:20 +03:00
|
|
|
loader.lazyRequireGetter(
|
|
|
|
this,
|
|
|
|
"AppConstants",
|
|
|
|
"resource://gre/modules/AppConstants.jsm",
|
|
|
|
true
|
|
|
|
);
|
2019-08-27 20:41:29 +03:00
|
|
|
loader.lazyRequireGetter(
|
|
|
|
this,
|
|
|
|
"PREFS",
|
|
|
|
"devtools/client/webconsole/constants",
|
|
|
|
true
|
|
|
|
);
|
2019-11-29 17:36:04 +03:00
|
|
|
loader.lazyRequireGetter(
|
|
|
|
this,
|
|
|
|
"START_IGNORE_ACTION",
|
|
|
|
"devtools/client/shared/redux/middleware/ignore",
|
|
|
|
true
|
|
|
|
);
|
2018-03-20 00:18:20 +03:00
|
|
|
|
2018-02-02 01:44:00 +03:00
|
|
|
const ZoomKeys = require("devtools/client/shared/zoom-keys");
|
2017-07-21 12:20:02 +03:00
|
|
|
|
2017-12-19 18:54:57 +03:00
|
|
|
const PREF_SIDEBAR_ENABLED = "devtools.webconsole.sidebarToggle";
|
2017-07-21 12:20:02 +03:00
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
/**
|
2019-02-15 11:16:37 +03:00
|
|
|
* A WebConsoleUI instance is an interactive console initialized *per target*
|
2017-04-26 18:10:04 +03:00
|
|
|
* that displays console log data as well as provides an interactive terminal to
|
|
|
|
* manipulate the target's document content.
|
|
|
|
*
|
2019-02-15 11:16:37 +03:00
|
|
|
* The WebConsoleUI is responsible for the actual Web Console UI
|
2017-04-26 18:10:04 +03:00
|
|
|
* implementation.
|
|
|
|
*/
|
2019-02-15 11:16:37 +03:00
|
|
|
class WebConsoleUI {
|
|
|
|
/*
|
2019-02-27 13:04:53 +03:00
|
|
|
* @param {WebConsole} hud: The WebConsole owner object.
|
2019-02-15 11:16:37 +03:00
|
|
|
*/
|
2019-02-27 13:04:53 +03:00
|
|
|
constructor(hud) {
|
|
|
|
this.hud = hud;
|
|
|
|
this.hudId = this.hud.hudId;
|
2019-07-29 11:44:08 +03:00
|
|
|
this.isBrowserConsole = this.hud.isBrowserConsole;
|
2019-10-03 01:50:25 +03:00
|
|
|
|
2019-09-06 16:38:37 +03:00
|
|
|
this.isBrowserToolboxConsole =
|
|
|
|
this.hud.currentTarget &&
|
|
|
|
this.hud.currentTarget.isParentProcess &&
|
|
|
|
!this.hud.currentTarget.isAddon;
|
2019-02-27 13:04:53 +03:00
|
|
|
this.window = this.hud.iframeWindow;
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
this._onPanelSelected = this._onPanelSelected.bind(this);
|
|
|
|
this._onChangeSplitConsoleState = this._onChangeSplitConsoleState.bind(
|
|
|
|
this
|
|
|
|
);
|
2019-11-07 18:05:10 +03:00
|
|
|
this._onTargetAvailable = this._onTargetAvailable.bind(this);
|
|
|
|
this._onTargetDestroyed = this._onTargetDestroyed.bind(this);
|
2019-02-15 11:16:37 +03:00
|
|
|
|
|
|
|
EventEmitter.decorate(this);
|
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
|
|
|
/**
|
2019-10-17 13:16:46 +03:00
|
|
|
* Getter for the WebConsoleFront.
|
2017-04-26 18:10:04 +03:00
|
|
|
* @type object
|
|
|
|
*/
|
2019-10-17 13:16:46 +03:00
|
|
|
get webConsoleFront() {
|
2019-08-08 16:23:03 +03:00
|
|
|
const proxy = this.getProxy();
|
|
|
|
|
|
|
|
if (!proxy) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-10-17 13:16:46 +03:00
|
|
|
return proxy.webConsoleFront;
|
2019-08-08 16:23:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the main target proxy, i.e. the proxy for MainProcessTarget in BrowserConsole,
|
|
|
|
* and the proxy for the target passed from the Toolbox to WebConsole.
|
|
|
|
*
|
|
|
|
* @returns {WebConsoleConnectionProxy}
|
|
|
|
*/
|
|
|
|
getProxy() {
|
|
|
|
return this.proxy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return all the proxies we're currently managing (i.e. the "main" one, and the
|
|
|
|
* possible additional ones).
|
|
|
|
*
|
2019-10-09 11:03:43 +03:00
|
|
|
* @param {Boolean} filterDisconnectedProxies: True by default, if false, this
|
|
|
|
* function also returns not-already-connected or already disconnected proxies.
|
|
|
|
*
|
2019-08-08 16:23:03 +03:00
|
|
|
* @returns {Array<WebConsoleConnectionProxy>}
|
|
|
|
*/
|
2019-10-09 11:03:43 +03:00
|
|
|
getAllProxies(filterDisconnectedProxies = true) {
|
2019-08-08 16:23:03 +03:00
|
|
|
let proxies = [this.getProxy()];
|
|
|
|
|
|
|
|
if (this.additionalProxies) {
|
2019-11-07 18:05:10 +03:00
|
|
|
proxies = proxies.concat([...this.additionalProxies.values()]);
|
2019-08-08 16:23:03 +03:00
|
|
|
}
|
|
|
|
|
2019-10-09 11:03:43 +03:00
|
|
|
// Ignore Fronts that are already destroyed
|
|
|
|
if (filterDisconnectedProxies) {
|
|
|
|
proxies = proxies.filter(proxy => {
|
2019-10-17 13:16:46 +03:00
|
|
|
return proxy.webConsoleFront && !!proxy.webConsoleFront.actorID;
|
2019-10-09 11:03:43 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-08-08 16:23:03 +03:00
|
|
|
return proxies;
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
|
|
|
/**
|
2019-02-15 11:16:37 +03:00
|
|
|
* Initialize the WebConsoleUI instance.
|
2017-04-26 18:10:04 +03:00
|
|
|
* @return object
|
|
|
|
* A promise object that resolves once the frame is ready to use.
|
|
|
|
*/
|
2019-08-08 16:23:03 +03:00
|
|
|
init() {
|
|
|
|
if (this._initializer) {
|
|
|
|
return this._initializer;
|
2017-10-27 09:50:57 +03:00
|
|
|
}
|
2019-08-08 16:23:03 +03:00
|
|
|
|
|
|
|
this._initializer = (async () => {
|
|
|
|
this._initUI();
|
2019-10-09 11:03:43 +03:00
|
|
|
await this._attachTargets();
|
2019-08-08 16:23:03 +03:00
|
|
|
await this.wrapper.init();
|
|
|
|
|
|
|
|
const id = WebConsoleUtils.supportsString(this.hudId);
|
|
|
|
if (Services.obs) {
|
|
|
|
Services.obs.notifyObservers(id, "web-console-created");
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
return this._initializer;
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
destroy() {
|
2019-07-25 09:43:52 +03:00
|
|
|
if (!this.hud) {
|
|
|
|
return;
|
2017-04-26 18:10:04 +03:00
|
|
|
}
|
2019-07-25 09:43:52 +03:00
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
this.React = this.ReactDOM = this.FrameView = null;
|
2019-07-26 18:49:53 +03:00
|
|
|
|
2019-11-29 17:36:04 +03:00
|
|
|
if (this.wrapper) {
|
|
|
|
this.wrapper.getStore().dispatch(START_IGNORE_ACTION);
|
|
|
|
}
|
|
|
|
|
2019-07-26 18:49:53 +03:00
|
|
|
if (this.outputNode) {
|
|
|
|
// We do this because it's much faster than letting React handle the ConsoleOutput
|
|
|
|
// unmounting.
|
|
|
|
this.outputNode.innerHTML = "";
|
|
|
|
}
|
|
|
|
|
2017-06-09 19:42:34 +03:00
|
|
|
if (this.jsterm) {
|
|
|
|
this.jsterm.destroy();
|
|
|
|
this.jsterm = null;
|
|
|
|
}
|
|
|
|
|
2019-08-22 11:10:06 +03:00
|
|
|
const toolbox = this.hud.toolbox;
|
2017-06-09 19:42:34 +03:00
|
|
|
if (toolbox) {
|
|
|
|
toolbox.off("webconsole-selected", this._onPanelSelected);
|
2018-04-02 10:10:53 +03:00
|
|
|
toolbox.off("split-console", this._onChangeSplitConsoleState);
|
|
|
|
toolbox.off("select", this._onChangeSplitConsoleState);
|
2017-06-09 19:42:34 +03:00
|
|
|
}
|
|
|
|
|
2019-11-07 18:05:10 +03:00
|
|
|
// Stop listening for targets
|
|
|
|
const targetList = this.hud.targetList;
|
|
|
|
targetList.unwatchTargets(
|
|
|
|
targetList.ALL_TYPES,
|
|
|
|
this._onTargetAvailable,
|
|
|
|
this._onTargetDestroy
|
|
|
|
);
|
2019-10-10 17:24:55 +03:00
|
|
|
|
2019-08-08 16:23:03 +03:00
|
|
|
for (const proxy of this.getAllProxies()) {
|
|
|
|
proxy.disconnect();
|
2017-04-26 18:10:04 +03:00
|
|
|
}
|
2019-08-08 16:23:03 +03:00
|
|
|
this.proxy = null;
|
|
|
|
this.additionalProxies = null;
|
2019-08-22 11:09:59 +03:00
|
|
|
|
|
|
|
// Nullify `hud` last as it nullify also target which is used on destroy
|
|
|
|
this.window = this.hud = this.wrapper = null;
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2018-06-03 02:44:08 +03:00
|
|
|
/**
|
|
|
|
* Clear the Web Console output.
|
|
|
|
*
|
|
|
|
* This method emits the "messages-cleared" notification.
|
|
|
|
*
|
|
|
|
* @param boolean clearStorage
|
|
|
|
* True if you want to clear the console messages storage associated to
|
|
|
|
* this Web Console.
|
2019-03-12 09:21:07 +03:00
|
|
|
* @param object event
|
|
|
|
* If the event exists, calls preventDefault on it.
|
2018-06-03 02:44:08 +03:00
|
|
|
*/
|
2019-03-12 09:21:07 +03:00
|
|
|
clearOutput(clearStorage, event) {
|
|
|
|
if (event) {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
2019-02-11 09:42:28 +03:00
|
|
|
if (this.wrapper) {
|
|
|
|
this.wrapper.dispatchMessagesClear();
|
2018-06-03 02:44:08 +03:00
|
|
|
}
|
2019-08-08 16:23:03 +03:00
|
|
|
this.clearNetworkRequests();
|
2018-06-03 02:44:08 +03:00
|
|
|
if (clearStorage) {
|
2019-08-08 16:23:03 +03:00
|
|
|
this.clearMessagesCache();
|
2018-06-03 02:44:08 +03:00
|
|
|
}
|
|
|
|
this.emit("messages-cleared");
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-06-03 02:44:08 +03:00
|
|
|
|
2019-08-08 16:23:03 +03:00
|
|
|
clearNetworkRequests() {
|
|
|
|
for (const proxy of this.getAllProxies()) {
|
2019-10-17 13:16:46 +03:00
|
|
|
proxy.webConsoleFront.clearNetworkRequests();
|
2019-08-08 16:23:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clearMessagesCache() {
|
|
|
|
for (const proxy of this.getAllProxies()) {
|
2019-10-17 13:16:46 +03:00
|
|
|
proxy.webConsoleFront.clearMessagesCache();
|
2019-08-08 16:23:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-03 02:44:08 +03:00
|
|
|
/**
|
|
|
|
* Remove all of the private messages from the Web Console output.
|
|
|
|
*
|
|
|
|
* This method emits the "private-messages-cleared" notification.
|
|
|
|
*/
|
|
|
|
clearPrivateMessages() {
|
2019-02-11 09:42:28 +03:00
|
|
|
if (this.wrapper) {
|
|
|
|
this.wrapper.dispatchPrivateMessagesClear();
|
2018-06-03 02:44:08 +03:00
|
|
|
this.emit("private-messages-cleared");
|
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-06-03 02:44:08 +03:00
|
|
|
|
2018-10-15 17:23:51 +03:00
|
|
|
inspectObjectActor(objectActor) {
|
2019-02-11 09:42:28 +03:00
|
|
|
this.wrapper.dispatchMessageAdd(
|
|
|
|
{
|
2018-10-15 17:23:51 +03:00
|
|
|
helperResult: {
|
|
|
|
type: "inspectObject",
|
2018-10-19 15:55:39 +03:00
|
|
|
object: objectActor,
|
2019-07-05 12:24:38 +03:00
|
|
|
},
|
2018-10-19 15:55:39 +03:00
|
|
|
},
|
2018-10-15 17:23:51 +03:00
|
|
|
true
|
|
|
|
);
|
2019-02-11 09:42:28 +03:00
|
|
|
return this.wrapper;
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-10-03 01:50:25 +03:00
|
|
|
getPanelWindow() {
|
|
|
|
return this.window;
|
|
|
|
}
|
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
logWarningAboutReplacedAPI() {
|
2019-08-22 11:10:13 +03:00
|
|
|
return this.hud.currentTarget.logWarningInPage(
|
2019-02-27 13:04:53 +03:00
|
|
|
l10n.getStr("ConsoleAPIDisabled"),
|
2017-08-04 18:32:27 +03:00
|
|
|
"ConsoleAPIDisabled"
|
|
|
|
);
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-05-03 00:52:34 +03:00
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
/**
|
|
|
|
* Setter for saving of network request and response bodies.
|
|
|
|
*
|
|
|
|
* @param boolean value
|
|
|
|
* The new value you want to set.
|
|
|
|
*/
|
2019-01-21 16:49:22 +03:00
|
|
|
async setSaveRequestAndResponseBodies(value) {
|
2019-10-17 13:16:46 +03:00
|
|
|
if (!this.webConsoleFront) {
|
2017-04-26 18:10:04 +03:00
|
|
|
// Don't continue if the webconsole disconnected.
|
2019-01-21 16:49:22 +03:00
|
|
|
return null;
|
2017-04-26 18:10:04 +03:00
|
|
|
}
|
|
|
|
|
2018-06-01 13:36:09 +03:00
|
|
|
const newValue = !!value;
|
|
|
|
const toSet = {
|
2017-04-26 18:10:04 +03:00
|
|
|
"NetworkMonitor.saveRequestAndResponseBodies": newValue,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Make sure the web console client connection is established first.
|
2019-10-17 13:16:46 +03:00
|
|
|
return this.webConsoleFront.setPreferences(toSet);
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect to the server using the remote debugging protocol.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @return object
|
2019-08-08 16:23:03 +03:00
|
|
|
* A promise object that is resolved/reject based on the proxies connections.
|
2017-04-26 18:10:04 +03:00
|
|
|
*/
|
2019-10-09 11:03:43 +03:00
|
|
|
async _attachTargets() {
|
2019-11-07 18:05:10 +03:00
|
|
|
this.additionalProxies = new Map();
|
|
|
|
// Listen for all target types, including:
|
|
|
|
// - frames, in order to get the parent process target
|
|
|
|
// which is considered as a frame rather than a process.
|
|
|
|
// - workers, for similar reason. When we open a toolbox
|
|
|
|
// for just a worker, the top level target is a worker target.
|
|
|
|
// - processes, as we want to spawn additional proxies for them.
|
|
|
|
await this.hud.targetList.watchTargets(
|
|
|
|
this.hud.targetList.ALL_TYPES,
|
|
|
|
this._onTargetAvailable,
|
|
|
|
this._onTargetDestroy
|
2019-08-27 20:41:29 +03:00
|
|
|
);
|
2019-11-07 18:05:10 +03:00
|
|
|
}
|
2019-08-27 20:41:29 +03:00
|
|
|
|
2019-11-07 18:05:10 +03:00
|
|
|
/**
|
|
|
|
* Called any time a new target is available.
|
|
|
|
* i.e. it was already existing or has just been created.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param string type
|
|
|
|
* One of the string of TargetList.TYPES to describe which
|
|
|
|
* type of target is available.
|
|
|
|
* @param Front targetFront
|
|
|
|
* The Front of the target that is available.
|
|
|
|
* This Front inherits from TargetMixin and is typically
|
|
|
|
* composed of a BrowsingContextTargetFront or ContentProcessTargetFront.
|
|
|
|
* @param boolean isTopLevel
|
|
|
|
* If true, means that this is the top level target.
|
|
|
|
* This typically happens on startup, providing the current
|
|
|
|
* top level target. But also on navigation, when we navigate
|
|
|
|
* to an URL which has to be loaded in a distinct process.
|
|
|
|
* A new top level target is created.
|
|
|
|
*/
|
|
|
|
async _onTargetAvailable(type, targetFront, isTopLevel) {
|
|
|
|
// This is a top level target. It may update on process switches
|
|
|
|
// when navigating to another domain.
|
|
|
|
if (isTopLevel) {
|
|
|
|
const fissionSupport = Services.prefs.getBoolPref(
|
|
|
|
PREFS.FEATURES.BROWSER_TOOLBOX_FISSION
|
|
|
|
);
|
|
|
|
const needContentProcessMessagesListener =
|
|
|
|
targetFront.isParentProcess && !targetFront.isAddon && !fissionSupport;
|
|
|
|
this.proxy = new WebConsoleConnectionProxy(
|
|
|
|
this,
|
|
|
|
targetFront,
|
|
|
|
needContentProcessMessagesListener
|
2019-10-10 17:24:55 +03:00
|
|
|
);
|
2019-11-07 18:05:10 +03:00
|
|
|
await this.proxy.connect();
|
|
|
|
return;
|
2019-10-10 17:24:55 +03:00
|
|
|
}
|
2019-11-07 18:05:10 +03:00
|
|
|
// Ignore frame targets, except the top level one, which is handled in the previous
|
|
|
|
// block.
|
|
|
|
if (type == this.hud.targetList.TYPES.FRAME) {
|
2019-10-10 17:24:55 +03:00
|
|
|
return;
|
|
|
|
}
|
2019-11-07 18:05:10 +03:00
|
|
|
const proxy = new WebConsoleConnectionProxy(this, targetFront);
|
|
|
|
this.additionalProxies.set(targetFront, proxy);
|
|
|
|
await proxy.connect();
|
|
|
|
}
|
2019-10-10 17:24:55 +03:00
|
|
|
|
2019-11-07 18:05:10 +03:00
|
|
|
/**
|
|
|
|
* Called any time a target has been destroyed.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* See _onTargetAvailable for param's description.
|
|
|
|
*/
|
|
|
|
_onTargetDestroyed(type, targetFront, isTopLevel) {
|
|
|
|
if (isTopLevel) {
|
|
|
|
this.proxy.disconnect();
|
|
|
|
this.proxy = null;
|
|
|
|
} else {
|
|
|
|
const proxy = this.additionalProxies.get(targetFront);
|
|
|
|
proxy.disconnect();
|
|
|
|
this.additionalProxies.delete(targetFront);
|
2019-08-08 16:23:03 +03:00
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
_initUI() {
|
2017-04-26 18:10:04 +03:00
|
|
|
this.document = this.window.document;
|
|
|
|
this.rootElement = this.document.documentElement;
|
|
|
|
|
2018-08-31 15:51:36 +03:00
|
|
|
this.outputNode = this.document.getElementById("app-wrapper");
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-08-22 11:10:06 +03:00
|
|
|
const toolbox = this.hud.toolbox;
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-07-18 14:40:38 +03:00
|
|
|
// Initialize module loader and load all the WebConsoleWrapper. The entire code-base
|
|
|
|
// doesn't need any extra privileges and runs entirely in content scope.
|
|
|
|
const WebConsoleWrapper = BrowserLoader({
|
|
|
|
baseURI: "resource://devtools/client/webconsole/",
|
|
|
|
window: this.window,
|
|
|
|
}).require("./webconsole-wrapper");
|
|
|
|
|
|
|
|
this.wrapper = new WebConsoleWrapper(
|
2019-02-27 13:04:53 +03:00
|
|
|
this.outputNode,
|
|
|
|
this,
|
|
|
|
toolbox,
|
|
|
|
this.document
|
|
|
|
);
|
2017-06-09 19:42:34 +03:00
|
|
|
|
|
|
|
this._initShortcuts();
|
2018-08-23 03:09:24 +03:00
|
|
|
this._initOutputSyntaxHighlighting();
|
2018-03-28 18:35:32 +03:00
|
|
|
|
|
|
|
if (toolbox) {
|
|
|
|
toolbox.on("webconsole-selected", this._onPanelSelected);
|
2018-04-02 10:10:53 +03:00
|
|
|
toolbox.on("split-console", this._onChangeSplitConsoleState);
|
|
|
|
toolbox.on("select", this._onChangeSplitConsoleState);
|
2018-03-28 18:35:32 +03:00
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
_initOutputSyntaxHighlighting() {
|
2018-08-23 03:09:24 +03:00
|
|
|
// Given a DOM node, we syntax highlight identically to how the input field
|
|
|
|
// looks. See https://codemirror.net/demo/runmode.html;
|
|
|
|
const syntaxHighlightNode = node => {
|
|
|
|
const editor = this.jsterm && this.jsterm.editor;
|
|
|
|
if (node && editor) {
|
|
|
|
node.classList.add("cm-s-mozilla");
|
|
|
|
editor.CodeMirror.runMode(
|
|
|
|
node.textContent,
|
|
|
|
"application/javascript",
|
|
|
|
node
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Use a Custom Element to handle syntax highlighting to avoid
|
|
|
|
// dealing with refs or innerHTML from React.
|
|
|
|
const win = this.window;
|
|
|
|
win.customElements.define(
|
|
|
|
"syntax-highlighted",
|
|
|
|
class extends win.HTMLElement {
|
|
|
|
connectedCallback() {
|
|
|
|
if (!this.connected) {
|
|
|
|
this.connected = true;
|
|
|
|
syntaxHighlightNode(this);
|
2019-07-05 12:24:38 +03:00
|
|
|
}
|
2018-08-23 03:09:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-08-23 03:09:24 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
_initShortcuts() {
|
2018-06-01 13:36:09 +03:00
|
|
|
const shortcuts = new KeyShortcuts({
|
2018-10-19 15:55:39 +03:00
|
|
|
window: this.window,
|
2017-06-09 19:42:34 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
let clearShortcut;
|
2018-03-20 00:18:20 +03:00
|
|
|
if (AppConstants.platform === "macosx") {
|
2019-03-12 09:21:07 +03:00
|
|
|
const alternativaClearShortcut = l10n.getStr(
|
|
|
|
"webconsole.clear.alternativeKeyOSX"
|
|
|
|
);
|
|
|
|
shortcuts.on(alternativaClearShortcut, event =>
|
|
|
|
this.clearOutput(true, event)
|
|
|
|
);
|
2017-06-09 19:42:34 +03:00
|
|
|
clearShortcut = l10n.getStr("webconsole.clear.keyOSX");
|
|
|
|
} else {
|
|
|
|
clearShortcut = l10n.getStr("webconsole.clear.key");
|
|
|
|
}
|
|
|
|
|
2019-03-12 09:21:07 +03:00
|
|
|
shortcuts.on(clearShortcut, event => this.clearOutput(true, event));
|
2017-06-09 19:42:34 +03:00
|
|
|
|
|
|
|
if (this.isBrowserConsole) {
|
2018-05-21 20:16:46 +03:00
|
|
|
// Make sure keyboard shortcuts work immediately after opening
|
|
|
|
// the Browser Console (Bug 1461366).
|
|
|
|
this.window.focus();
|
2017-06-09 19:42:34 +03:00
|
|
|
shortcuts.on(
|
|
|
|
l10n.getStr("webconsole.close.key"),
|
2019-09-30 12:51:44 +03:00
|
|
|
this.window.close.bind(this.window)
|
2018-02-20 00:52:56 +03:00
|
|
|
);
|
2017-06-09 19:42:34 +03:00
|
|
|
|
2019-04-26 19:40:57 +03:00
|
|
|
ZoomKeys.register(this.window, shortcuts);
|
2018-05-03 21:23:33 +03:00
|
|
|
shortcuts.on("CmdOrCtrl+Alt+R", quickRestart);
|
2017-12-19 18:54:57 +03:00
|
|
|
} else if (Services.prefs.getBoolPref(PREF_SIDEBAR_ENABLED)) {
|
2018-03-07 12:17:03 +03:00
|
|
|
shortcuts.on("Esc", event => {
|
2019-03-15 14:43:41 +03:00
|
|
|
this.wrapper.dispatchSidebarClose();
|
|
|
|
if (this.jsterm) {
|
2018-04-16 11:23:39 +03:00
|
|
|
this.jsterm.focus();
|
2017-12-19 18:54:57 +03:00
|
|
|
}
|
|
|
|
});
|
2017-06-09 19:42:34 +03:00
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-05-03 21:23:33 +03:00
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
/**
|
|
|
|
* Release an actor.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param string actor
|
|
|
|
* The actor ID you want to release.
|
|
|
|
*/
|
2019-08-08 16:23:03 +03:00
|
|
|
releaseActor(actor) {
|
|
|
|
const proxy = this.getProxy();
|
|
|
|
if (!proxy) {
|
|
|
|
return null;
|
2017-04-26 18:10:04 +03:00
|
|
|
}
|
2019-08-08 16:23:03 +03:00
|
|
|
|
|
|
|
return proxy.releaseActor(actor);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {String} expression
|
|
|
|
* @param {Object} options
|
|
|
|
* @returns {Promise}
|
|
|
|
*/
|
|
|
|
evaluateJSAsync(expression, options) {
|
2019-10-17 13:16:46 +03:00
|
|
|
return this.getProxy().webConsoleFront.evaluateJSAsync(expression, options);
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2019-10-03 01:50:25 +03:00
|
|
|
getLongString(grip) {
|
2019-10-17 13:16:46 +03:00
|
|
|
this.getProxy().webConsoleFront.getString(grip);
|
2019-10-03 01:50:25 +03:00
|
|
|
}
|
|
|
|
|
2017-04-26 18:10:04 +03:00
|
|
|
/**
|
2018-03-28 18:35:32 +03:00
|
|
|
* Sets the focus to JavaScript input field when the web console tab is
|
|
|
|
* selected or when there is a split console present.
|
|
|
|
* @private
|
|
|
|
*/
|
2019-02-15 11:16:37 +03:00
|
|
|
_onPanelSelected() {
|
2019-03-15 14:43:41 +03:00
|
|
|
// We can only focus when we have the jsterm reference. This is fine because if the
|
|
|
|
// jsterm is not mounted yet, it will be focused in JSTerm's componentDidMount.
|
|
|
|
if (this.jsterm) {
|
|
|
|
this.jsterm.focus();
|
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-03-28 18:35:32 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
_onChangeSplitConsoleState() {
|
2019-02-11 09:42:28 +03:00
|
|
|
this.wrapper.dispatchSplitConsoleCloseButtonToggle();
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-04-02 10:10:53 +03:00
|
|
|
|
2018-03-28 18:35:32 +03:00
|
|
|
/**
|
2017-04-26 18:10:04 +03:00
|
|
|
* Handler for the tabNavigated notification.
|
|
|
|
*
|
|
|
|
* @param string event
|
|
|
|
* Event name.
|
|
|
|
* @param object packet
|
|
|
|
* Notification packet received from the server.
|
|
|
|
*/
|
2019-02-15 11:16:37 +03:00
|
|
|
async handleTabNavigated(packet) {
|
2018-03-14 20:17:07 +03:00
|
|
|
if (!packet.nativeConsoleAPI) {
|
2017-04-26 18:10:04 +03:00
|
|
|
this.logWarningAboutReplacedAPI();
|
|
|
|
}
|
2017-10-24 21:56:58 +03:00
|
|
|
|
2018-03-14 20:17:07 +03:00
|
|
|
// Wait for completion of any async dispatch before notifying that the console
|
|
|
|
// is fully updated after a page reload
|
2019-02-11 09:42:28 +03:00
|
|
|
await this.wrapper.waitAsyncDispatches();
|
2018-03-14 20:17:07 +03:00
|
|
|
this.emit("reloaded");
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2018-03-14 20:17:07 +03:00
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
handleTabWillNavigate(packet) {
|
2019-02-11 09:42:28 +03:00
|
|
|
this.wrapper.dispatchTabWillNavigate(packet);
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2019-10-03 01:50:25 +03:00
|
|
|
|
|
|
|
getInputCursor() {
|
|
|
|
return this.jsterm && this.jsterm.getSelectionStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
getJsTermTooltipAnchor() {
|
|
|
|
return this.outputNode.querySelector(".CodeMirror-cursor");
|
|
|
|
}
|
|
|
|
|
|
|
|
attachRef(id, node) {
|
|
|
|
this[id] = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the FrameActor ID given a frame depth, or the selected one if no
|
|
|
|
* frame depth given.
|
|
|
|
*
|
2019-10-17 13:16:46 +03:00
|
|
|
* @return { frameActor: String|null, webConsoleFront: WebConsoleFront }:
|
2019-10-03 01:50:25 +03:00
|
|
|
* frameActor is the FrameActor ID for the given frame depth
|
|
|
|
* (or the selected frame if it exists), null if no frame was found.
|
2019-10-17 13:16:46 +03:00
|
|
|
* webConsoleFront is the front for the thread the frame is associated with.
|
2019-10-03 01:50:25 +03:00
|
|
|
*/
|
|
|
|
getFrameActor() {
|
|
|
|
const state = this.hud.getDebuggerFrames();
|
|
|
|
if (!state) {
|
2019-10-17 13:16:46 +03:00
|
|
|
return { frameActor: null, webConsoleFront: this.webConsoleFront };
|
2019-10-03 01:50:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const grip = state.frames[state.selected];
|
|
|
|
|
|
|
|
if (!grip) {
|
2019-10-17 13:16:46 +03:00
|
|
|
return { frameActor: null, webConsoleFront: this.webConsoleFront };
|
2019-10-03 01:50:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
frameActor: grip.actor,
|
2019-10-17 13:16:46 +03:00
|
|
|
webConsoleFront: state.target.activeConsole,
|
2019-10-03 01:50:25 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-10-28 23:36:11 +03:00
|
|
|
getSelectedNodeActor() {
|
2019-10-29 03:36:23 +03:00
|
|
|
const front = this.getSelectedNodeFront();
|
|
|
|
return front ? front.actorID : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
getSelectedNodeFront() {
|
2019-10-03 01:50:25 +03:00
|
|
|
const inspectorSelection = this.hud.getInspectorSelection();
|
2019-10-29 03:36:23 +03:00
|
|
|
return inspectorSelection ? inspectorSelection.nodeFront : null;
|
2019-10-03 01:50:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
onMessageHover(type, message) {
|
|
|
|
this.emit("message-hover", type, message);
|
|
|
|
}
|
2019-02-15 11:16:37 +03:00
|
|
|
}
|
2017-04-26 18:10:04 +03:00
|
|
|
|
2018-05-03 21:23:33 +03:00
|
|
|
/* This is the same as DevelopmentHelpers.quickRestart, but it runs in all
|
|
|
|
* builds (even official). This allows a user to do a restart + session restore
|
|
|
|
* with Ctrl+Shift+J (open Browser Console) and then Ctrl+Shift+R (restart).
|
|
|
|
*/
|
|
|
|
function quickRestart() {
|
|
|
|
const { Cc, Ci } = require("chrome");
|
|
|
|
Services.obs.notifyObservers(null, "startupcache-invalidate");
|
2018-06-01 13:36:09 +03:00
|
|
|
const env = Cc["@mozilla.org/process/environment;1"].getService(
|
2018-05-03 21:23:33 +03:00
|
|
|
Ci.nsIEnvironment
|
|
|
|
);
|
|
|
|
env.set("MOZ_DISABLE_SAFE_MODE_KEY", "1");
|
|
|
|
Services.startup.quit(
|
|
|
|
Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-15 11:16:37 +03:00
|
|
|
exports.WebConsoleUI = WebConsoleUI;
|