summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/resource/legacy-listeners
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/commands/resource/legacy-listeners')
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/console-messages.js59
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/css-changes.js28
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/error-messages.js62
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/moz.build14
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/platform-messages.js44
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/reflow.js24
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/root-node.js61
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/source.js88
-rw-r--r--devtools/shared/commands/resource/legacy-listeners/thread-states.js81
9 files changed, 461 insertions, 0 deletions
diff --git a/devtools/shared/commands/resource/legacy-listeners/console-messages.js b/devtools/shared/commands/resource/legacy-listeners/console-messages.js
new file mode 100644
index 0000000000..ae3f81b4df
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/console-messages.js
@@ -0,0 +1,59 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
+ // Allow the top level target unconditionnally.
+ // Also allow frame, but only in content toolbox, i.e. still ignore them in
+ // the context of the browser toolbox as we inspect messages via the process
+ // targets
+ const listenForFrames = targetCommand.descriptorFront.isTabDescriptor;
+
+ // Allow workers when messages aren't dispatched to the main thread.
+ const listenForWorkers =
+ !targetCommand.rootFront.traits
+ .workerConsoleApiMessagesDispatchedToMainThread;
+
+ const acceptTarget =
+ targetFront.isTopLevel ||
+ targetFront.targetType === targetCommand.TYPES.PROCESS ||
+ (targetFront.targetType === targetCommand.TYPES.FRAME && listenForFrames) ||
+ (targetFront.targetType === targetCommand.TYPES.WORKER && listenForWorkers);
+
+ if (!acceptTarget) {
+ return;
+ }
+
+ const webConsoleFront = await targetFront.getFront("console");
+ if (webConsoleFront.isDestroyed()) {
+ return;
+ }
+
+ // Request notifying about new messages
+ await webConsoleFront.startListeners(["ConsoleAPI"]);
+
+ // Fetch already existing messages
+ // /!\ The actor implementation requires to call startListeners(ConsoleAPI) first /!\
+ const { messages } = await webConsoleFront.getCachedMessages(["ConsoleAPI"]);
+
+ for (const message of messages) {
+ message.resourceType = ResourceCommand.TYPES.CONSOLE_MESSAGE;
+ }
+ onAvailable(messages);
+
+ // Forward new message events
+ webConsoleFront.on("consoleAPICall", message => {
+ // Ignore console messages that are cloned from the content process
+ // (they aren't relevant to toolboxes still using legacy listeners)
+ if (message.clonedFromContentProcess) {
+ return;
+ }
+
+ message.resourceType = ResourceCommand.TYPES.CONSOLE_MESSAGE;
+ onAvailable([message]);
+ });
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/css-changes.js b/devtools/shared/commands/resource/legacy-listeners/css-changes.js
new file mode 100644
index 0000000000..e9f3e17075
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/css-changes.js
@@ -0,0 +1,28 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetFront, onAvailable }) {
+ if (!targetFront.hasActor("changes")) {
+ return;
+ }
+
+ const changesFront = await targetFront.getFront("changes");
+
+ // Get all changes collected up to this point by the ChangesActor on the server,
+ // then fire each change as "add-change".
+ const changes = await changesFront.allChanges();
+ await onAvailable(changes.map(change => toResource(change)));
+
+ changesFront.on("add-change", change => onAvailable([toResource(change)]));
+};
+
+function toResource(change) {
+ return Object.assign(change, {
+ resourceType: ResourceCommand.TYPES.CSS_CHANGE,
+ });
+}
diff --git a/devtools/shared/commands/resource/legacy-listeners/error-messages.js b/devtools/shared/commands/resource/legacy-listeners/error-messages.js
new file mode 100644
index 0000000000..5ba898c917
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/error-messages.js
@@ -0,0 +1,62 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+const { MESSAGE_CATEGORY } = require("resource://devtools/shared/constants.js");
+
+module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
+ // Allow the top level target unconditionnally.
+ // Also allow frame, but only in content toolbox, i.e. still ignore them in
+ // the context of the browser toolbox as we inspect messages via the process
+ // targets
+ // Also ignore workers as they are not supported yet. (see bug 1592584)
+ const listenForFrames = targetCommand.descriptorFront.isTabDescriptor;
+ const isAllowed =
+ targetFront.isTopLevel ||
+ targetFront.targetType === targetCommand.TYPES.PROCESS ||
+ (targetFront.targetType === targetCommand.TYPES.FRAME && listenForFrames);
+
+ if (!isAllowed) {
+ return;
+ }
+
+ const webConsoleFront = await targetFront.getFront("console");
+ if (webConsoleFront.isDestroyed()) {
+ return;
+ }
+
+ // Request notifying about new messages. Here the "PageError" type start listening for
+ // both actual PageErrors (emitted as "pageError" events) as well as LogMessages (
+ // emitted as "logMessage" events). This function only set up the listener on the
+ // webConsoleFront for "pageError".
+ await webConsoleFront.startListeners(["PageError"]);
+
+ // Fetch already existing messages
+ // /!\ The actor implementation requires to call startListeners("PageError") first /!\
+ let { messages } = await webConsoleFront.getCachedMessages(["PageError"]);
+
+ // On server < v79, we're also getting CSS Messages that we need to filter out.
+ messages = messages.filter(
+ message => message.pageError.category !== MESSAGE_CATEGORY.CSS_PARSER
+ );
+
+ messages.forEach(message => {
+ message.resourceType = ResourceCommand.TYPES.ERROR_MESSAGE;
+ });
+ // Cached messages don't have the same shape as live messages,
+ // so we need to transform them.
+ onAvailable(messages);
+
+ webConsoleFront.on("pageError", message => {
+ // On server < v79, we're getting CSS Messages that we need to filter out.
+ if (message.pageError.category === MESSAGE_CATEGORY.CSS_PARSER) {
+ return;
+ }
+
+ message.resourceType = ResourceCommand.TYPES.ERROR_MESSAGE;
+ onAvailable([message]);
+ });
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/moz.build b/devtools/shared/commands/resource/legacy-listeners/moz.build
new file mode 100644
index 0000000000..6ffb469891
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/moz.build
@@ -0,0 +1,14 @@
+# 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/.
+
+DevToolsModules(
+ "console-messages.js",
+ "css-changes.js",
+ "error-messages.js",
+ "platform-messages.js",
+ "reflow.js",
+ "root-node.js",
+ "source.js",
+ "thread-states.js",
+)
diff --git a/devtools/shared/commands/resource/legacy-listeners/platform-messages.js b/devtools/shared/commands/resource/legacy-listeners/platform-messages.js
new file mode 100644
index 0000000000..729696275e
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/platform-messages.js
@@ -0,0 +1,44 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
+ // Only allow the top level target and processes.
+ // Frames can be ignored as logMessage are never sent to them anyway.
+ // Also ignore workers as they are not supported yet. (see bug 1592584)
+ const isAllowed =
+ targetFront.isTopLevel ||
+ targetFront.targetType === targetCommand.TYPES.PROCESS;
+ if (!isAllowed) {
+ return;
+ }
+
+ const webConsoleFront = await targetFront.getFront("console");
+ if (webConsoleFront.isDestroyed()) {
+ return;
+ }
+
+ // Request notifying about new messages. Here the "PageError" type start listening for
+ // both actual PageErrors (emitted as "pageError" events) as well as LogMessages (
+ // emitted as "logMessage" events). This function only set up the listener on the
+ // webConsoleFront for "logMessage".
+ await webConsoleFront.startListeners(["PageError"]);
+
+ // Fetch already existing messages
+ // /!\ The actor implementation requires to call startListeners("PageError") first /!\
+ const { messages } = await webConsoleFront.getCachedMessages(["LogMessage"]);
+
+ for (const message of messages) {
+ message.resourceType = ResourceCommand.TYPES.PLATFORM_MESSAGE;
+ }
+ onAvailable(messages);
+
+ webConsoleFront.on("logMessage", message => {
+ message.resourceType = ResourceCommand.TYPES.PLATFORM_MESSAGE;
+ onAvailable([message]);
+ });
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/reflow.js b/devtools/shared/commands/resource/legacy-listeners/reflow.js
new file mode 100644
index 0000000000..63802f510d
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/reflow.js
@@ -0,0 +1,24 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetFront, onAvailable }) {
+ if (!targetFront.getTrait("isBrowsingContext")) {
+ // The reflows only work with BrowsingContext targets
+ return;
+ }
+ const reflowFront = await targetFront.getFront("reflow");
+ reflowFront.on("reflows", reflows =>
+ onAvailable([
+ {
+ resourceType: ResourceCommand.TYPES.REFLOW,
+ reflows,
+ },
+ ])
+ );
+ await reflowFront.start();
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/root-node.js b/devtools/shared/commands/resource/legacy-listeners/root-node.js
new file mode 100644
index 0000000000..6fa2bcbf22
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/root-node.js
@@ -0,0 +1,61 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetFront, onAvailable, onDestroyed }) {
+ // XXX: When watching root node for a non top-level target, this will also
+ // ensure the inspector & walker fronts for the target are initialized.
+ // This also implies that we call reparentRemoteFrame on the new walker, which
+ // will create the link between the parent frame NodeFront and the inner
+ // document NodeFront.
+ //
+ // This is not something that will work when the resource is moved to the
+ // server. When it becomes a server side resource, a RootNode would be emitted
+ // directly by the target actor.
+ //
+ // This probably means that the root node resource cannot remain a NodeFront.
+ // It should not be a front and the client should be responsible for
+ // retrieving the corresponding NodeFront.
+ //
+ // The other thing that we are missing with this patch is that we should only
+ // create inspector & walker fronts (and call reparentRemoteFrame) when we get
+ // a RootNode which is directly under an iframe node which is currently
+ // visible and tracked in the markup view.
+ //
+ // For instance, with the following markup:
+ // html
+ // body
+ // div
+ // iframe
+ // remote doc
+ //
+ // If the markup view only sees nodes down to `div`, then the client is not
+ // currently tracking the nodeFront for the `iframe`, and getting a new root
+ // node for the remote document should NOT force the iframe to be tracked on
+ // on the client.
+ //
+ // When we get a RootNode resource, we will need a way to check this before
+ // initializing & reparenting the walker.
+ //
+ if (!targetFront.getTrait("isBrowsingContext")) {
+ // The root-node resource is only available on browsing-context targets.
+ return;
+ }
+
+ const inspectorFront = await targetFront.getFront("inspector");
+ inspectorFront.walker.on("root-available", node => {
+ node.resourceType = ResourceCommand.TYPES.ROOT_NODE;
+ return onAvailable([node]);
+ });
+
+ inspectorFront.walker.on("root-destroyed", node => {
+ node.resourceType = ResourceCommand.TYPES.ROOT_NODE;
+ return onDestroyed([node]);
+ });
+
+ await inspectorFront.walker.watchRootNode();
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/source.js b/devtools/shared/commands/resource/legacy-listeners/source.js
new file mode 100644
index 0000000000..45ee62f70f
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/source.js
@@ -0,0 +1,88 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+/**
+ * Emit SOURCE resources, which represents a Javascript source and has the following attributes set on "available":
+ *
+ * - introductionType {null|String}: A string indicating how this source code was introduced into the system.
+ * This will typically be set to "scriptElement", "eval", ...
+ * But this may have many other values:
+ * https://searchfox.org/mozilla-central/rev/ac142717cc067d875e83e4b1316f004f6e063a46/dom/script/ScriptLoader.cpp#2628-2639
+ * https://searchfox.org/mozilla-central/search?q=symbol:_ZN2JS14CompileOptions19setIntroductionTypeEPKc&redirect=false
+ * https://searchfox.org/mozilla-central/rev/ac142717cc067d875e83e4b1316f004f6e063a46/devtools/server/actors/source.js#160-169
+ * - sourceMapBaseURL {String}: Base URL where to look for a source map.
+ * This isn't the source map URL.
+ * - sourceMapURL {null|String}: URL of the source map, if there is one.
+ * - url {null|String}: URL of the source, if it relates to a particular URL.
+ * Evaled sources won't have any related URL.
+ * - isBlackBoxed {Boolean}: Specifying whether the source actor's 'black-boxed' flag is set.
+ * - extensionName {null|String}: If the source comes from an add-on, the add-on name.
+ */
+module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
+ const isBrowserToolbox =
+ targetCommand.descriptorFront.isBrowserProcessDescriptor;
+ const isNonTopLevelFrameTarget =
+ !targetFront.isTopLevel &&
+ targetFront.targetType === targetCommand.TYPES.FRAME;
+
+ if (isBrowserToolbox && isNonTopLevelFrameTarget) {
+ // In the BrowserToolbox, non-top-level frame targets are already
+ // debugged via content-process targets.
+ return;
+ }
+
+ const threadFront = await targetFront.getFront("thread");
+
+ // Use a list of all notified SourceFront as we don't have a newSource event for all sources
+ // but we sometime get sources notified both via newSource event *and* sources() method...
+ // We store actor ID instead of SourceFront as it appears that multiple SourceFront for the same
+ // actor are created...
+ const sourcesActorIDCache = new Set();
+
+ // Forward new sources (but also existing ones, see next comment)
+ threadFront.on("newSource", ({ source }) => {
+ if (sourcesActorIDCache.has(source.actor)) {
+ return;
+ }
+ sourcesActorIDCache.add(source.actor);
+ // source is a SourceActor's form, add the resourceType attribute on it
+ source.resourceType = ResourceCommand.TYPES.SOURCE;
+ onAvailable([source]);
+ });
+
+ // Forward already existing sources
+ // Note that calling `sources()` will end up emitting `newSource` event for all existing sources.
+ // But not in some cases, for example, when the thread is already paused.
+ // (And yes, it means that already existing sources can be transfered twice over the wire)
+ //
+ // Also, browser_ext_devtools_inspectedWindow_targetSwitch.js creates many top level targets,
+ // for which the SourceMapURLService will fetch sources. But these targets are destroyed while
+ // the test is running and when they are, we purge all pending requests, including this one.
+ // So ignore any error if this request failed on destruction.
+ let sources;
+ try {
+ sources = await threadFront.sources();
+ } catch (e) {
+ if (threadFront.isDestroyed()) {
+ return;
+ }
+ throw e;
+ }
+
+ // Note that `sources()` doesn't encapsulate SourceFront into a `source` attribute
+ // while `newSource` event does.
+ sources = sources.filter(source => {
+ return !sourcesActorIDCache.has(source.actor);
+ });
+ for (const source of sources) {
+ sourcesActorIDCache.add(source.actor);
+ // source is a SourceActor's form, add the resourceType attribute on it
+ source.resourceType = ResourceCommand.TYPES.SOURCE;
+ }
+ onAvailable(sources);
+};
diff --git a/devtools/shared/commands/resource/legacy-listeners/thread-states.js b/devtools/shared/commands/resource/legacy-listeners/thread-states.js
new file mode 100644
index 0000000000..42c922072a
--- /dev/null
+++ b/devtools/shared/commands/resource/legacy-listeners/thread-states.js
@@ -0,0 +1,81 @@
+/* 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 ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
+
+module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
+ const isBrowserToolbox =
+ targetCommand.descriptorFront.isBrowserProcessDescriptor;
+ const isNonTopLevelFrameTarget =
+ !targetFront.isTopLevel &&
+ targetFront.targetType === targetCommand.TYPES.FRAME;
+
+ if (isBrowserToolbox && isNonTopLevelFrameTarget) {
+ // In the BrowserToolbox, non-top-level frame targets are already
+ // debugged via content-process targets.
+ return;
+ }
+
+ // Wait for the thread actor to be attached, otherwise getFront(thread) will throw for worker targets
+ // This is because worker target are still kind of descriptors and are only resolved into real target
+ // after being attached. And the thread actor ID is only retrieved and available after being attached.
+ await targetFront.onThreadAttached;
+
+ if (targetFront.isDestroyed()) {
+ return;
+ }
+ const threadFront = await targetFront.getFront("thread");
+
+ let isInterrupted = false;
+ const onPausedPacket = packet => {
+ // If paused by an explicit interrupt, which are generated by the
+ // slow script dialog and internal events such as setting
+ // breakpoints, ignore the event.
+ const { why } = packet;
+ if (why.type === "interrupted" && !why.onNext) {
+ isInterrupted = true;
+ return;
+ }
+
+ // Ignore attached events because they are not useful to the user.
+ if (why.type == "alreadyPaused" || why.type == "attached") {
+ return;
+ }
+
+ onAvailable([
+ {
+ resourceType: ResourceCommand.TYPES.THREAD_STATE,
+ state: "paused",
+ why,
+ frame: packet.frame,
+ },
+ ]);
+ };
+ threadFront.on("paused", onPausedPacket);
+
+ threadFront.on("resumed", packet => {
+ // NOTE: the client suppresses resumed events while interrupted
+ // to prevent unintentional behavior.
+ // see [client docs](devtools/client/debugger/src/client/README.md#interrupted) for more information.
+ if (isInterrupted) {
+ isInterrupted = false;
+ return;
+ }
+
+ onAvailable([
+ {
+ resourceType: ResourceCommand.TYPES.THREAD_STATE,
+ state: "resumed",
+ },
+ ]);
+ });
+
+ // Notify about already paused thread
+ const pausedPacket = threadFront.getLastPausePacket();
+ if (pausedPacket) {
+ onPausedPacket(pausedPacket);
+ }
+};