<commit_before>/* See license.txt for terms of usage */

"use strict";

const { Cc, Ci, Cu } = require("chrome");

// Placeholder for tracing API.
// xxxHonza: conditional logging needed
var Trace = {
  sysout: function(msg, ...args) {
    try {
      // Cu.reportError(msg + " --- " + args.map(x => x.toString()).join(" ; "));

      let scope = {};
      Cu["import"]("resource://fbtrace/firebug-trace-service.js", scope);
      let FBTrace = scope.traceConsoleService.getTracer("extensions.firebug");
      FBTrace.sysout(msg, args);
    }
    catch(err) {
      Cu.reportError(msg + " --- " + args.map(x => x.toString()).join(" ; "));
    }
  }
};

// SDK API
const Events = require("sdk/system/events.js");
const { getTabForContentWindow, getBrowserForTab }  = require("sdk/tabs/utils");
const base64 = require("sdk/base64");
const WindowUtils = require("sdk/window/utils");

// Debugger
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});

// Devtools helper API
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const DevToolsUtils = devtools["require"]("devtools/toolkit/DevToolsUtils.js");
const NetworkHelper = devtools["require"]("devtools/toolkit/webconsole/network-helper");

// Remote Debugging Protocol API
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableHeaders = ["x-chromelogger-data"];

// xxxHonza: duplicated in logger-actor module.
function expectState(expectedState, method) {
  return function(...args) {
    if (this.state !== expectedState) {
      let msg = "Wrong State: Expected '" + expectedState + "', but current " +
        "state is '" + this.state + "'";
      return Promise.reject(new Error(msg));
    }

    try {
      return method.apply(this, args);
    } catch (err) {
      Trace.sysout("monitorActor EXCEPTION " + err, err);
    }
  };
}

const actorTypeName = "firebugMonitor";

/**
 * @actor TODO: documentation
 */
var MonitorActor = ActorClass(
/** @lends MonitorActor */
{
  typeName: actorTypeName,

  // Initialization

  initialize: function(conn, parent) {
    Trace.sysout("monitorActor.initialize;", this);

    Actor.prototype.initialize.call(this, conn);

    this.parent = parent;
    this.state = "detached";
    this.onExamineResponse = this.onExamineResponse.bind(this);
    this.onAttachChild = this.onAttachChild.bind(this);
    this.onDetachChild = this.onDetachChild.bind(this);

    let manager = getMessageManager();
    manager.addMessageListener("http-monitor:attach-child", this.onAttachChild);
    manager.addMessageListener("http-monitor:detach-child", this.onDetachChild);

    // Attached child loggers.
    this.targets = new Map();
  },

  /**
   * The destroy is only called automatically by the framework (parent actor)
   * if an actor is instantiated by a parent actor.
   */
  destroy: function() {
    Trace.sysout("monitorActor.destroy; state: " + this.state, arguments);

    if (this.state === "attached") {
      this.detach();
    }

    Actor.prototype.destroy.call(this);
  },

  /**
   * Automatically executed by the framework when the parent connection
   * is closed.
   */
  disconnect: function() {
    Trace.sysout("monitorActor.disconnect; state: " + this.state, arguments);

    if (this.state === "attached") {
      this.detach();
    }
  },

  /**
   * Attach to this actor. Executed when the front (client) is attaching
   * to this actor in order to receive server side logs.
   *
   * The main responsibility of this method is registering a listener for
   * "http-on-examine-response" events.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("monitorActor.attach;", arguments);

    Events.on("http-on-examine-response", this.onExamineResponse);

    this.state = "attached";
  }), {
    request: {},
    response: {
      type: "attached"
    }
  }),

  /**
   * Detach from this actor. Executed when the front (client) detaches
   * from this actor since it isn't interested in server side logs
   * any more. So, let's remove the "http-on-examine-response" listener.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("monitorActor.detach;", arguments);

    this.state = "detached";

    Events.off("http-on-examine-response", this.onExamineResponse);
  }), {
    request: {},
    response: {
      type: "detached"
    }
  }),

  onAttachChild: function(event) {
    Trace.sysout("monitorActor.onAttachChild; Child logger attached", event);

    // Collect child loggers. The 'target' (XULElement) represents the
    // browser element associated with the content that this message
    // came from.
    this.targets.set(event.data.winId, event.target);
  },

  onDetachChild: function(event) {
    Trace.sysout("monitorActor.onAttachChild; Child logger detached", event);

    this.targets.remove(event.data.winId, event.target);
  },

  // HTTP Observer

  // xxxHonza: this code is duplicated in {@MonitorActor} can we
  // share it somehow?
  onExamineResponse: function(event) {
    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
    let tab = getTabFromHttpChannel(subject);

    Trace.sysout("monitorActor.onExamineResponse; " + httpChannel.name +
      ", tab: " + tab, httpChannel);

    if (!tab) {
      return;
    }

    let requestFrame = getTopFrameElementForRequest(httpChannel);
    let win = requestFrame._contentWindow;
    let winId = WindowUtils.getOuterId(win);

    let target = this.targets.get(winId);
    if (!target) {
      return;
    }

    let { messageManager } = target;
    if (!messageManager) {
      return;
    }

    let headers = [];

    httpChannel.visitResponseHeaders((header, value) => {
      header = header.toLowerCase();
      if (acceptableHeaders.indexOf(header) !== -1) {
        headers.push({header: header, value: value});
      }
    });

    messageManager.sendAsyncMessage("http-monitor:examine-headers", {
      headers: headers
    });
<commit_msg>#232 couple of fixes for monitor actor<commit_after>/* See license.txt for terms of usage */

"use strict";

const { Cc, Ci, Cu } = require("chrome");

// Placeholder for tracing API.
// xxxHonza: conditional logging needed
var Trace = {
  sysout: function(msg, ...args) {
    try {
      // Cu.reportError(msg + " --- " + args.map(x => x.toString()).join(" ; "));

      let scope = {};
      Cu["import"]("resource://fbtrace/firebug-trace-service.js", scope);
      let FBTrace = scope.traceConsoleService.getTracer("extensions.firebug");
      FBTrace.sysout(msg, args);
    }
    catch(err) {
      Cu.reportError(msg + " --- " + args.map(x => x.toString()).join(" ; "));
    }
  }
};

// SDK API
const Events = require("sdk/system/events.js");
const { getTabForContentWindow, getBrowserForTab }  = require("sdk/tabs/utils");
const base64 = require("sdk/base64");
const WindowUtils = require("sdk/window/utils");

// Debugger
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});

// Devtools helper API
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const DevToolsUtils = devtools["require"]("devtools/toolkit/DevToolsUtils.js");
const NetworkHelper = devtools["require"]("devtools/toolkit/webconsole/network-helper");

// Remote Debugging Protocol API
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableHeaders = ["x-chromelogger-data"];

// xxxHonza: duplicated in logger-actor module.
function expectState(expectedState, method) {
  return function(...args) {
    if (this.state !== expectedState) {
      let msg = "Wrong State: Expected '" + expectedState + "', but current " +
        "state is '" + this.state + "'";
      return Promise.reject(new Error(msg));
    }

    try {
      return method.apply(this, args);
    } catch (err) {
      Trace.sysout("monitorActor EXCEPTION " + err, err);
    }
  };
}

const actorTypeName = "firebugMonitor";

/**
 * @actor TODO: documentation
 */
var MonitorActor = ActorClass(
/** @lends MonitorActor */
{
  typeName: actorTypeName,

  // Initialization

  initialize: function(conn, parent) {
    Trace.sysout("monitorActor.initialize;", this);

    Actor.prototype.initialize.call(this, conn);

    this.parent = parent;
    this.state = "detached";
    this.onExamineResponse = this.onExamineResponse.bind(this);
    this.onAttachChild = this.onAttachChild.bind(this);
    this.onDetachChild = this.onDetachChild.bind(this);

    let manager = getMessageManager();
    manager.addMessageListener("http-monitor:attach-child", this.onAttachChild);
    manager.addMessageListener("http-monitor:detach-child", this.onDetachChild);

    // Attached child loggers.
    this.targets = new Map();
  },

  /**
   * The destroy is only called automatically by the framework (parent actor)
   * if an actor is instantiated by a parent actor.
   */
  destroy: function() {
    Trace.sysout("monitorActor.destroy; state: " + this.state, arguments);

    if (this.state === "attached") {
      this.detach();
    }

    Actor.prototype.destroy.call(this);
  },

  /**
   * Automatically executed by the framework when the parent connection
   * is closed.
   */
  disconnect: function() {
    Trace.sysout("monitorActor.disconnect; state: " + this.state, arguments);

    if (this.state === "attached") {
      this.detach();
    }
  },

  /**
   * Attach to this actor. Executed when the front (client) is attaching
   * to this actor in order to receive server side logs.
   *
   * The main responsibility of this method is registering a listener for
   * "http-on-examine-response" events.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("monitorActor.attach;", arguments);

    Events.on("http-on-examine-response", this.onExamineResponse);

    this.state = "attached";
  }), {
    request: {},
    response: {
      type: "attached"
    }
  }),

  /**
   * Detach from this actor. Executed when the front (client) detaches
   * from this actor since it isn't interested in server side logs
   * any more. So, let's remove the "http-on-examine-response" listener.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("monitorActor.detach;", arguments);

    this.state = "detached";

    Events.off("http-on-examine-response", this.onExamineResponse);
  }), {
    request: {},
    response: {
      type: "detached"
    }
  }),

  onAttachChild: function(event) {
    Trace.sysout("monitorActor.onAttachChild; Child logger attached", event);

    // Collect child loggers. The 'target' (XULElement) represents the
    // browser element associated with the content that this message
    // came from.
    this.targets.set(event.data.winId, event.target);
  },

  onDetachChild: function(event) {
    Trace.sysout("monitorActor.onAttachChild; Child logger detached", event);

    this.targets.delete(event.data.winId, event.target);
  },

  // HTTP Observer

  // xxxHonza: this code is duplicated in {@MonitorActor} can we
  // share it somehow?
  onExamineResponse: function(event) {
    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);

    Trace.sysout("monitorActor.onExamineResponse; " + httpChannel.name,
      httpChannel);

    let requestFrame = getTopFrameElementForRequest(httpChannel);
    if (!requestFrame) {
      return;
    }

    let win = requestFrame._contentWindow;
    let winId = WindowUtils.getOuterId(win);

    let target = this.targets.get(winId);
    if (!target) {
      return;
    }

    let { messageManager } = target;
    if (!messageManager) {
      return;
    }

    let headers = [];

    httpChannel.visitResponseHeaders((header, value) => {
      header = header.toLowerCase();
      if (acceptableHeaders.indexOf(header) !== -1) {
        headers.push({header: header, value: value});
      }
    });

    messageManager.sendAsyncMessage("http-monitor:examine-headers", {
      headers: headers
    });

{'fraction_non_alphanumeric': 0.14581304771178188, 'fraction_numerical': 0.0010548523206751054, 'mean_word_length': 3.316987740805604, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7230662', 'n_tokens_mistral': 3830, 'n_tokens_neox': 3708, 'n_words': 853}