diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /devtools/server/actors/webconsole/listeners/console-api.js | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/server/actors/webconsole/listeners/console-api.js')
-rw-r--r-- | devtools/server/actors/webconsole/listeners/console-api.js | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/devtools/server/actors/webconsole/listeners/console-api.js b/devtools/server/actors/webconsole/listeners/console-api.js new file mode 100644 index 0000000000..3e5d0bc52f --- /dev/null +++ b/devtools/server/actors/webconsole/listeners/console-api.js @@ -0,0 +1,255 @@ +/* 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 { + CONSOLE_WORKER_IDS, + WebConsoleUtils, +} = require("resource://devtools/server/actors/webconsole/utils.js"); + +// The window.console API observer + +/** + * The window.console API observer. This allows the window.console API messages + * to be sent to the remote Web Console instance. + * + * @constructor + * @param nsIDOMWindow window + * Optional - the window object for which we are created. This is used + * for filtering out messages that belong to other windows. + * @param Function handler + * This function is invoked with one argument, the Console API message that comes + * from the observer service, whenever a relevant console API call is received. + * @param object filteringOptions + * Optional - The filteringOptions that this listener should listen to: + * - addonId: filter console messages based on the addonId. + * - excludeMessagesBoundToWindow: Set to true to filter out messages that + * are bound to a specific window. + * - matchExactWindow: Set to true to match the messages on a specific window (when + * `window` is defined) and not on the whole window tree. + */ +class ConsoleAPIListener { + constructor( + window, + handler, + { addonId, excludeMessagesBoundToWindow, matchExactWindow } = {} + ) { + this.window = window; + this.handler = handler; + this.addonId = addonId; + this.excludeMessagesBoundToWindow = excludeMessagesBoundToWindow; + this.matchExactWindow = matchExactWindow; + if (this.window) { + this.innerWindowId = WebConsoleUtils.getInnerWindowId(this.window); + } + } + + QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]); + + /** + * The content window for which we listen to window.console API calls. + * @type nsIDOMWindow + */ + window = null; + + /** + * The function which is notified of window.console API calls. It is invoked with one + * argument: the console API call object that comes from the ConsoleAPIStorage service. + * + * @type function + */ + handler = null; + + /** + * The addonId that we listen for. If not null then only messages from this + * console will be returned. + */ + addonId = null; + + /** + * Initialize the window.console API listener. + */ + init() { + const ConsoleAPIStorage = Cc[ + "@mozilla.org/consoleAPI-storage;1" + ].getService(Ci.nsIConsoleAPIStorage); + + // Note that the listener is process-wide. We will filter the messages as + // needed, see onConsoleAPILogEvent(). + this.onConsoleAPILogEvent = this.onConsoleAPILogEvent.bind(this); + ConsoleAPIStorage.addLogEventListener( + this.onConsoleAPILogEvent, + // We create a principal here to get the privileged principal of this + // script. Note that this is importantly *NOT* the principal of the + // content we are observing, as that would not have access to the + // message object created in ConsoleAPIStorage.jsm's scope. + Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal) + ); + } + + /** + * The console API message listener. When messages are received from the + * ConsoleAPIStorage service we forward them to the remote Web Console instance. + * + * @param object message + * The message object receives from the ConsoleAPIStorage service. + */ + onConsoleAPILogEvent(message) { + if (!this.handler) { + return; + } + + // Here, wrappedJSObject is not a security wrapper but a property defined + // by the XPCOM component which allows us to unwrap the XPCOM interface and + // access the underlying JSObject. + const apiMessage = message.wrappedJSObject; + + if (!this.isMessageRelevant(apiMessage)) { + return; + } + + this.handler(apiMessage); + } + + /** + * Given a message, return true if this window should show it and false + * if it should be ignored. + * + * @param message + * The message from the Storage Service + * @return bool + * Do we care about this message? + */ + isMessageRelevant(message) { + const workerType = WebConsoleUtils.getWorkerType(message); + + if (this.window && workerType === "ServiceWorker") { + // For messages from Service Workers, message.ID is the + // scope, which can be used to determine whether it's controlling + // a window. + const scope = message.ID; + + if (!this.window.shouldReportForServiceWorkerScope(scope)) { + return false; + } + } + + // innerID can be of different type: + // - a number if the message is bound to a specific window + // - a worker type ([Shared|Service]Worker) if the message comes from a worker + // - a JSM filename + // if we want to filter on a specific window, ignore all non-worker messages that + // don't have a proper window id (for now, we receive the worker messages from the + // main process so we still want to get them, although their innerID isn't a number). + if (!workerType && typeof message.innerID !== "number" && this.window) { + return false; + } + + // Don't show ChromeWorker messages on WindowGlobal targets + if (workerType && this.window && message.chromeContext) { + return false; + } + + if (typeof message.innerID == "number") { + if ( + this.excludeMessagesBoundToWindow && + // If innerID is 0, the message isn't actually bound to a window. + message.innerID + ) { + return false; + } + + if (this.window) { + const matchesWindow = this.matchExactWindow + ? this.innerWindowId === message.innerID + : WebConsoleUtils.getInnerWindowIDsForFrames(this.window).includes( + message.innerID + ); + + if (!matchesWindow) { + // Not the same window! + return false; + } + } + } + + if (this.addonId) { + // ConsoleAPI.jsm messages contains a consoleID, (and it is currently + // used in Addon SDK add-ons), the standard 'console' object + // (which is used in regular webpages and in WebExtensions pages) + // contains the originAttributes of the source document principal. + + // Filtering based on the originAttributes used by + // the Console API object. + if (message.addonId == this.addonId) { + return true; + } + + // Filtering based on the old-style consoleID property used by + // the legacy Console JSM module. + if (message.consoleID && message.consoleID == `addon/${this.addonId}`) { + return true; + } + + return false; + } + + return true; + } + + /** + * Get the cached messages for the current inner window and its (i)frames. + * + * @param boolean [includePrivate=false] + * Tells if you want to also retrieve messages coming from private + * windows. Defaults to false. + * @return array + * The array of cached messages. + */ + getCachedMessages(includePrivate = false) { + let messages = []; + const ConsoleAPIStorage = Cc[ + "@mozilla.org/consoleAPI-storage;1" + ].getService(Ci.nsIConsoleAPIStorage); + + // if !this.window, we're in a browser console. Retrieve all events + // for filtering based on privacy. + if (!this.window) { + messages = ConsoleAPIStorage.getEvents(); + } else if (this.matchExactWindow) { + messages = ConsoleAPIStorage.getEvents(this.innerWindowId); + } else { + WebConsoleUtils.getInnerWindowIDsForFrames(this.window).forEach(id => { + messages = messages.concat(ConsoleAPIStorage.getEvents(id)); + }); + } + + CONSOLE_WORKER_IDS.forEach(id => { + messages = messages.concat(ConsoleAPIStorage.getEvents(id)); + }); + + messages = messages.filter(msg => { + return this.isMessageRelevant(msg); + }); + + if (includePrivate) { + return messages; + } + + return messages.filter(m => !m.private); + } + + /** + * Destroy the console API listener. + */ + destroy() { + const ConsoleAPIStorage = Cc[ + "@mozilla.org/consoleAPI-storage;1" + ].getService(Ci.nsIConsoleAPIStorage); + ConsoleAPIStorage.removeLogEventListener(this.onConsoleAPILogEvent); + this.window = this.handler = null; + } +} +exports.ConsoleAPIListener = ConsoleAPIListener; |