diff options
Diffstat (limited to 'devtools/server/actors/webconsole/content-process-forward.js')
-rw-r--r-- | devtools/server/actors/webconsole/content-process-forward.js | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/devtools/server/actors/webconsole/content-process-forward.js b/devtools/server/actors/webconsole/content-process-forward.js new file mode 100644 index 0000000000..69720d51b3 --- /dev/null +++ b/devtools/server/actors/webconsole/content-process-forward.js @@ -0,0 +1,143 @@ +/* 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +ChromeUtils.defineModuleGetter( + this, + "E10SUtils", + "resource://gre/modules/E10SUtils.jsm" +); + +/* + * The message manager has an upper limit on message sizes that it can + * reliably forward to the parent so we limit the size of console log event + * messages that we forward here. The web console is local and receives the + * full console message, but addons subscribed to console event messages + * in the parent receive the truncated version. Due to fragmentation, + * messages as small as 1MB have resulted in IPC allocation failures on + * 32-bit platforms. To limit IPC allocation sizes, console.log messages + * with arguments with total size > MSG_MGR_CONSOLE_MAX_SIZE (bytes) have + * their arguments completely truncated. MSG_MGR_CONSOLE_VAR_SIZE is an + * approximation of how much space (in bytes) a JS non-string variable will + * require in the manager's implementation. For strings, we use 2 bytes per + * char. The console message URI and function name are limited to + * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate + * the exact amount of space the message manager implementation will require + * for a given message so this is imperfect. + */ +const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB +const MSG_MGR_CONSOLE_VAR_SIZE = 8; +const MSG_MGR_CONSOLE_INFO_MAX = 1024; + +function ContentProcessForward() { + Services.obs.addObserver(this, "console-api-log-event"); + Services.obs.addObserver(this, "xpcom-shutdown"); + Services.cpmm.addMessageListener( + "DevTools:StopForwardingContentProcessMessage", + this + ); +} +ContentProcessForward.prototype = { + QueryInterface: ChromeUtils.generateQI([ + "nsIObserver", + "nsISupportsWeakReference", + ]), + + receiveMessage(message) { + if (message.name == "DevTools:StopForwardingContentProcessMessage") { + this.uninit(); + } + }, + + observe(subject, topic, data) { + switch (topic) { + case "console-api-log-event": { + const consoleMsg = subject.wrappedJSObject; + + const msgData = { + ...consoleMsg, + arguments: [], + filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX), + functionName: + consoleMsg.functionName && + consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX), + // Prevents cyclic object error when using msgData in sendAsyncMessage + wrappedJSObject: null, + }; + + // We can't send objects over the message manager, so we sanitize + // them out, replacing those arguments with "<unavailable>". + const unavailString = "<unavailable>"; + const unavailStringLength = unavailString.length * 2; // 2-bytes per char + + // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE, + // replace all arguments with "<truncated>". + let totalArgLength = 0; + + // Walk through the arguments, checking the type and size. + for (let arg of consoleMsg.arguments) { + if ( + (typeof arg == "object" || typeof arg == "function") && + arg !== null + ) { + if ( + Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE + ) { + // For OOP extensions: we want the developer to be able to see the + // logs in the Browser Console. When the Addon Toolbox will be more + // prominent we can revisit. + try { + // If the argument is clonable, then send it as-is. If + // cloning fails, fall back to the unavailable string. + arg = Cu.cloneInto(arg, {}); + } catch (e) { + arg = unavailString; + } + } else { + arg = unavailString; + } + totalArgLength += unavailStringLength; + } else if (typeof arg == "string") { + totalArgLength += arg.length * 2; // 2-bytes per char + } else { + totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE; + } + + if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) { + msgData.arguments.push(arg); + } else { + // arguments take up too much space + msgData.arguments = ["<truncated>"]; + break; + } + } + + Services.cpmm.sendAsyncMessage("Console:Log", msgData); + break; + } + + case "xpcom-shutdown": + this.uninit(); + break; + } + }, + + uninit() { + Services.obs.removeObserver(this, "console-api-log-event"); + Services.obs.removeObserver(this, "xpcom-shutdown"); + Services.cpmm.removeMessageListener( + "DevTools:StopForwardingContentProcessMessage", + this + ); + }, +}; + +// loadProcessScript loads in all processes, including the parent, +// in which we don't need any forwarding +if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + new ContentProcessForward(); +} |