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

"use strict";


/**
 * Note: This file is loaded on the back-end (can be a remote device) in
 * a sandbox that doesn't have all usual globals and modules (introduced by
 * e.g. Add-on SDK). Also, remote devices can use various (old) versions of
 * Add-on SDK and the platform. So, be careful when using external
 * dependencies they don't have to exist (such as FBTrace).
 */

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

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

const Events = require("sdk/system/events.js");
const tabUtils = require("sdk/tabs/utils");
const base64 = require("sdk/base64");

const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableLoggerHeaders = ["X-ChromeLogger-Data"];

/**
 * A method decorator that ensures the actor is in the expected state before
 * proceeding. If the actor is not in the expected state, the decorated method
 * returns a rejected promise.
 *
 * @param String expectedState
 *        The expected state.
 *
 * @param Function method
 *        The actor method to proceed with when the actor is in the expected
 *        state.
 *
 * @returns Function
 *          The decorated method.
 */
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));
    }

    return method.apply(this, args);
  };
}

const actorTypeName = "firebugLoggerActor";

/**
 * @actor The actor is responsible for detecting server side logs
 * within HTTP headers and sending them to the client.
 */
var LoggerActor = ActorClass(
/** @lends LoggerActor */
{
  typeName: actorTypeName,

  // Initialization

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

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

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

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

  destroy: function() {
    Trace.sysout("loggerActor.destroy;", arguments);

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

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

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

  /**
   * Attach to this actor.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("loggerActor.attach;", arguments);

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

  /**
   * Detach from this actor.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("loggerActor.detach;", arguments);

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

  // HTTP Observer
  onExamineResponse: function(event) {
    Trace.sysout("loggerActor.onExamineResponse");

    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
    let tab = getTabFromHttpChannel(subject);

    if (!tab) {
      return;
    }

    // xxxHonza: we need to filer out responses coming from different
    // browser tabs. FIX ME

    let parsedMessages = [];

    httpChannel.visitResponseHeaders((header, value) => {
      if (acceptableLoggerHeaders.indexOf(header) !== -1) {
        let parsedMessage = this.parse(header, value);
        parsedMessages.push(parsedMessage);
      }
    });

    if (!parsedMessages.length) {
      return;
    }<commit_msg>Issue #108: filter out responses coming from different tabs<commit_after>/* See license.txt for terms of usage */

"use strict";


/**
 * Note: This file is loaded on the back-end (can be a remote device) in
 * a sandbox that doesn't have all usual globals and modules (introduced by
 * e.g. Add-on SDK). Also, remote devices can use various (old) versions of
 * Add-on SDK and the platform. So, be careful when using external
 * dependencies they don't have to exist (such as FBTrace).
 */

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

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

const Events = require("sdk/system/events.js");
const tabUtils = require("sdk/tabs/utils");
const base64 = require("sdk/base64");

const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const protocol = devtools["require"]("devtools/server/protocol");
const { method, RetVal, ActorClass, Actor } = protocol;

// TODO support FirePHP
const acceptableLoggerHeaders = ["X-ChromeLogger-Data"];

/**
 * A method decorator that ensures the actor is in the expected state before
 * proceeding. If the actor is not in the expected state, the decorated method
 * returns a rejected promise.
 *
 * @param String expectedState
 *        The expected state.
 *
 * @param Function method
 *        The actor method to proceed with when the actor is in the expected
 *        state.
 *
 * @returns Function
 *          The decorated method.
 */
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));
    }

    return method.apply(this, args);
  };
}

const actorTypeName = "firebugLoggerActor";

/**
 * @actor The actor is responsible for detecting server side logs
 * within HTTP headers and sending them to the client.
 */
var LoggerActor = ActorClass(
/** @lends LoggerActor */
{
  typeName: actorTypeName,

  // Initialization

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

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

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

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

  destroy: function() {
    Trace.sysout("loggerActor.destroy;", arguments);

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

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

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

  /**
   * Attach to this actor.
   */
  attach: method(expectState("detached", function() {
    Trace.sysout("loggerActor.attach;", arguments);

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

  /**
   * Detach from this actor.
   */
  detach: method(expectState("attached", function() {
    Trace.sysout("loggerActor.detach;", arguments);

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

  // HTTP Observer
  onExamineResponse: function(event) {
    Trace.sysout("loggerActor.onExamineResponse");

    let { subject } = event;
    let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
    let tab = getTabFromHttpChannel(subject);

    if (!tab) {
      return;
    }

    // Filter out responses coming from different tabs.
    if (tab.linkedBrowser !== this.parent.browser) {
      return;
    }

    let parsedMessages = [];

    httpChannel.visitResponseHeaders((header, value) => {
      if (acceptableLoggerHeaders.indexOf(header) !== -1) {
        let parsedMessage = this.parse(header, value);
        parsedMessages.push(parsedMessage);
      }
    });

    if (!parsedMessages.length) {
      return;
    }
{'fraction_non_alphanumeric': 0.14201040280633845, 'fraction_numerical': 0.001572517237208177, 'mean_word_length': 3.2119205298013247, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '3203687', 'n_tokens_mistral': 2516, 'n_tokens_neox': 2434, 'n_words': 669}