summaryrefslogtreecommitdiffstats
path: root/devtools/shared/webconsole/server-logger-monitor.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/webconsole/server-logger-monitor.js')
-rw-r--r--devtools/shared/webconsole/server-logger-monitor.js191
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;