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"
|
|
|
|
);
|
Bug 1579090 - Support ObjectFronts in WebConsole. r=Honza.
Since we may now have ObjectFronts in console messages as
well as in evaluation results, we need to make the webconsole
consume those fronts.
And because we have ObjectFronts, we can now avoid using the
DebuggerClient to release the actors, and simply call front.release.
This simplifies how we track created object, since we only need
to release "root" objects, which will also cause all the sub-fronts
to be released as well. Sadly, this was causing some test failures
in mochitests because some objects were released while the connection
was closed, so we now emit an event when all the actors are released,
which we track in tests to wait until everything is over.
As a side effect, we need to change how we handle stubs for
our tests, since fronts have cycical references and can't
be serialized directly. In order to handle that, we serialize
all front as a plain object with a `_grip` property, containing
the object grip. In the stub file, we don't expose the grips
directly, but Maps that contain those stubs "rehydrated": when
a _grip object is encountered, it's turned back into a front.
Differential Revision: https://phabricator.services.mozilla.com/D54509
--HG--
extra : moz-landing-system : lando
2019-12-04 12:03:53 +03:00
|
|
|
const {
|
|
|
|
getAdHocFrontOrPrimitiveGrip,
|
|
|
|
} = require("devtools/shared/fronts/object");
|
2019-07-18 14:40:38 +03:00
|
|
|
|
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
|
|
|
|
);
|
2019-12-06 11:29:20 +03:00
|
|
|
const ConsoleCommands = require("devtools/client/webconsole/commands.js");
|
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-12-06 11:29:20 +03:00
|
|
|
|
|
|
|
this._commands = new ConsoleCommands({
|
|
|
|
debuggerClient: this.hud.currentTarget.client,
|
|
|
|
proxy: this.getProxy(),
|
|
|
|
threadFront: this.hud.toolbox && this.hud.toolbox.threadFront,
|
|
|
|
currentTarget: this.hud.currentTarget,
|
|
|
|
});
|
|
|
|
|
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
|
|
|
}
|
2020-01-09 18:09:55 +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();
|
2020-01-09 18:09:55 +03:00
|
|
|
this.emit("private-messages-cleared");
|
2018-06-03 02:44:08 +03:00
|
|
|
}
|
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) {
|
Bug 1579090 - Support ObjectFronts in WebConsole. r=Honza.
Since we may now have ObjectFronts in console messages as
well as in evaluation results, we need to make the webconsole
consume those fronts.
And because we have ObjectFronts, we can now avoid using the
DebuggerClient to release the actors, and simply call front.release.
This simplifies how we track created object, since we only need
to release "root" objects, which will also cause all the sub-fronts
to be released as well. Sadly, this was causing some test failures
in mochitests because some objects were released while the connection
was closed, so we now emit an event when all the actors are released,
which we track in tests to wait until everything is over.
As a side effect, we need to change how we handle stubs for
our tests, since fronts have cycical references and can't
be serialized directly. In order to handle that, we serialize
all front as a plain object with a `_grip` property, containing
the object grip. In the stub file, we don't expose the grips
directly, but Maps that contain those stubs "rehydrated": when
a _grip object is encountered, it's turned back into a front.
Differential Revision: https://phabricator.services.mozilla.com/D54509
--HG--
extra : moz-landing-system : lando
2019-12-04 12:03:53 +03:00
|
|
|
const webConsoleFront = this.webConsoleFront;
|
2019-02-11 09:42:28 +03:00
|
|
|
this.wrapper.dispatchMessageAdd(
|
|
|
|
{
|
2018-10-15 17:23:51 +03:00
|
|
|
helperResult: {
|
|
|
|
type: "inspectObject",
|
Bug 1579090 - Support ObjectFronts in WebConsole. r=Honza.
Since we may now have ObjectFronts in console messages as
well as in evaluation results, we need to make the webconsole
consume those fronts.
And because we have ObjectFronts, we can now avoid using the
DebuggerClient to release the actors, and simply call front.release.
This simplifies how we track created object, since we only need
to release "root" objects, which will also cause all the sub-fronts
to be released as well. Sadly, this was causing some test failures
in mochitests because some objects were released while the connection
was closed, so we now emit an event when all the actors are released,
which we track in tests to wait until everything is over.
As a side effect, we need to change how we handle stubs for
our tests, since fronts have cycical references and can't
be serialized directly. In order to handle that, we serialize
all front as a plain object with a `_grip` property, containing
the object grip. In the stub file, we don't expose the grips
directly, but Maps that contain those stubs "rehydrated": when
a _grip object is encountered, it's turned back into a front.
Differential Revision: https://phabricator.services.mozilla.com/D54509
--HG--
extra : moz-landing-system : lando
2019-12-04 12:03:53 +03:00
|
|
|
object:
|
|
|
|
objectActor && objectActor.getGrip
|
|
|
|
? objectActor
|
|
|
|
: getAdHocFrontOrPrimitiveGrip(objectActor, webConsoleFront),
|
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.
|
|
|
|
*/
|
2019-12-12 13:41:27 +03:00
|
|
|
async _onTargetAvailable({ type, targetFront, isTopLevel }) {
|
2019-11-07 18:05:10 +03:00
|
|
|
// 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
|
2019-12-19 11:50:41 +03:00
|
|
|
// block. Also ignore workers as they are not supported yet. (see bug 1592584)
|
|
|
|
if (type != this.hud.targetList.TYPES.PROCESS) {
|
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.
|
|
|
|
*/
|
2019-12-12 13:41:27 +03:00
|
|
|
_onTargetDestroyed({ type, targetFront, isTopLevel }) {
|
2019-11-07 18:05:10 +03:00
|
|
|
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,
|
2019-12-27 15:23:47 +03:00
|
|
|
}).require("devtools/client/webconsole/webconsole-wrapper");
|
2019-07-18 14:40:38 +03:00
|
|
|
|
|
|
|
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
|
|
|
|
2019-10-03 01:50:25 +03:00
|
|
|
getLongString(grip) {
|
2020-01-06 18:27:49 +03:00
|
|
|
return 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
|
|
|
*/
|
2019-12-13 17:04:46 +03:00
|
|
|
async getFrameActor() {
|
2019-10-03 01:50:25 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-12-13 17:04:46 +03:00
|
|
|
const webConsoleFront = await state.target.getFront("console");
|
|
|
|
|
2019-10-03 01:50:25 +03:00
|
|
|
return {
|
|
|
|
frameActor: grip.actor,
|
2019-12-13 17:04:46 +03:00
|
|
|
webConsoleFront,
|
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;
|