diff options
Diffstat (limited to 'devtools/shared/webconsole/server-logger-monitor.js')
-rw-r--r-- | devtools/shared/webconsole/server-logger-monitor.js | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/devtools/shared/webconsole/server-logger-monitor.js b/devtools/shared/webconsole/server-logger-monitor.js new file mode 100644 index 000000000..9cc2682ea --- /dev/null +++ b/devtools/shared/webconsole/server-logger-monitor.js @@ -0,0 +1,191 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft= javascript ts=2 et sw=2 tw=80: */ +/* 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 {Ci} = require("chrome"); +const Services = require("Services"); + +const {makeInfallible} = require("devtools/shared/DevToolsUtils"); + +loader.lazyGetter(this, "NetworkHelper", () => require("devtools/shared/webconsole/network-helper")); + +// Helper tracer. Should be generic sharable by other modules (bug 1171927) +const trace = { + log: function (...args) { + } +}; + +const acceptableHeaders = ["x-chromelogger-data"]; + +/** + * This object represents HTTP events observer. It's intended to be + * used in e10s enabled browser only. + * + * Since child processes can't register HTTP event observer they use + * this module to do the observing in the parent process. This monitor + * is loaded through DebuggerServerConnection.setupInParent() that is + * executed from within the child process. The execution is done by + * {@ServerLoggingListener}. The monitor listens to HTTP events and + * forwards it into the right child process. + * + * Read more about the architecture: + * https://github.com/mozilla/gecko-dev/blob/fx-team/devtools/server/docs/actor-e10s-handling.md + */ +var ServerLoggerMonitor = { + // Initialization + + initialize: function () { + this.onChildMessage = this.onChildMessage.bind(this); + this.onExamineResponse = this.onExamineResponse.bind(this); + + // Set of registered child frames (loggers). + this.targets = new Set(); + }, + + // Parent Child Relationship + + attach: makeInfallible(function ({ mm, prefix }) { + trace.log("ServerLoggerMonitor.attach; ", arguments); + + let setMessageManager = newMM => { + if (mm) { + mm.removeMessageListener("debug:server-logger", this.onChildMessage); + } + mm = newMM; + if (mm) { + mm.addMessageListener("debug:server-logger", this.onChildMessage); + } + }; + + // Start listening for messages from the {@ServerLogger} actor + // living in the child process. + setMessageManager(mm); + + return { + onBrowserSwap: setMessageManager, + onDisconnected: () => { + trace.log("ServerLoggerMonitor.onDisconnectChild; ", arguments); + setMessageManager(null); + } + }; + }), + + // Child Message Handling + + onChildMessage: function (msg) { + let method = msg.data.method; + + trace.log("ServerLoggerMonitor.onChildMessage; ", method, msg); + + switch (method) { + case "attachChild": + return this.onAttachChild(msg); + case "detachChild": + return this.onDetachChild(msg); + default: + trace.log("Unknown method name: ", method); + return undefined; + } + }, + + onAttachChild: function (event) { + let target = event.target; + let size = this.targets.size; + + trace.log("ServerLoggerMonitor.onAttachChild; size: ", size, target); + + // If this is the first child attached, register global HTTP observer. + if (!size) { + trace.log("ServerLoggerMonitor.onAttatchChild; Add HTTP Observer"); + Services.obs.addObserver(this.onExamineResponse, + "http-on-examine-response", false); + } + + // Collect child loggers. The frame element where the + // window/document lives. + this.targets.add(target); + }, + + onDetachChild: function (event) { + let target = event.target; + this.targets.delete(target); + + let size = this.targets.size; + trace.log("ServerLoggerMonitor.onDetachChild; size: ", size, target); + + // If this is the last child process attached, unregister + // the global HTTP observer. + if (!size) { + trace.log("ServerLoggerMonitor.onDetachChild; Remove HTTP Observer"); + Services.obs.removeObserver(this.onExamineResponse, + "http-on-examine-response", false); + } + }, + + // HTTP Observer + + onExamineResponse: makeInfallible(function (subject, topic) { + let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); + + trace.log("ServerLoggerMonitor.onExamineResponse; ", httpChannel.name, + this.targets); + + // Ignore requests from chrome or add-on code when we are monitoring + // content. + if (!httpChannel.loadInfo && + httpChannel.loadInfo.loadingDocument === null && + httpChannel.loadInfo.loadingPrincipal === + Services.scriptSecurityManager.getSystemPrincipal()) { + return; + } + + let requestFrame = NetworkHelper.getTopFrameForRequest(httpChannel); + if (!requestFrame) { + return; + } + + // Ignore requests from parent frames that aren't registered. + if (!this.targets.has(requestFrame)) { + return; + } + + let headers = []; + + httpChannel.visitResponseHeaders((header, value) => { + header = header.toLowerCase(); + if (acceptableHeaders.indexOf(header) !== -1) { + headers.push({header: header, value: value}); + } + }); + + if (!headers.length) { + return; + } + + let { messageManager } = requestFrame; + messageManager.sendAsyncMessage("debug:server-logger", { + method: "examineHeaders", + headers: headers, + }); + + trace.log("ServerLoggerMonitor.onExamineResponse; headers ", + headers.length, ", ", headers); + }), +}; + +/** + * Executed automatically by the framework. + */ +function setupParentProcess(event) { + return ServerLoggerMonitor.attach(event); +} + +// Monitor initialization. +ServerLoggerMonitor.initialize(); + +// Exports from this module +exports.setupParentProcess = setupParentProcess; |