summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/network-monitor/channel-event-sink.js
blob: 8ff00302f9a55e7211a214eedcf96b1960a4e46f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* 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 { ComponentUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/ComponentUtils.sys.mjs"
);

/**
 * This is a nsIChannelEventSink implementation that monitors channel redirects and
 * informs the registered "collectors" about the old and new channels.
 */
const SINK_CLASS_DESCRIPTION = "NetworkMonitor Channel Event Sink";
const SINK_CLASS_ID = Components.ID("{e89fa076-c845-48a8-8c45-2604729eba1d}");
const SINK_CONTRACT_ID = "@mozilla.org/network/monitor/channeleventsink;1";
const SINK_CATEGORY_NAME = "net-channel-event-sinks";

class ChannelEventSink {
  constructor() {
    this.wrappedJSObject = this;
    this.collectors = new Set();
  }

  QueryInterface = ChromeUtils.generateQI(["nsIChannelEventSink"]);

  registerCollector(collector) {
    this.collectors.add(collector);
  }

  unregisterCollector(collector) {
    this.collectors.delete(collector);

    if (this.collectors.size == 0) {
      ChannelEventSinkFactory.unregister();
    }
  }

  // eslint-disable-next-line no-shadow
  asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
    for (const collector of this.collectors) {
      try {
        collector.onChannelRedirect(oldChannel, newChannel, flags);
      } catch (ex) {
        console.error(
          "ChannelEventSink collector's 'onChannelRedirect' threw an exception",
          ex
        );
      }
    }
    callback.onRedirectVerifyCallback(Cr.NS_OK);
  }
}

const ChannelEventSinkFactory =
  ComponentUtils.generateSingletonFactory(ChannelEventSink);

ChannelEventSinkFactory.register = function () {
  const registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
  if (registrar.isCIDRegistered(SINK_CLASS_ID)) {
    return;
  }

  registrar.registerFactory(
    SINK_CLASS_ID,
    SINK_CLASS_DESCRIPTION,
    SINK_CONTRACT_ID,
    ChannelEventSinkFactory
  );

  Services.catMan.addCategoryEntry(
    SINK_CATEGORY_NAME,
    SINK_CONTRACT_ID,
    SINK_CONTRACT_ID,
    false,
    true
  );
};

ChannelEventSinkFactory.unregister = function () {
  const registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
  registrar.unregisterFactory(SINK_CLASS_ID, ChannelEventSinkFactory);

  Services.catMan.deleteCategoryEntry(
    SINK_CATEGORY_NAME,
    SINK_CONTRACT_ID,
    false
  );
};

ChannelEventSinkFactory.getService = function () {
  // Make sure the ChannelEventSink service is registered before accessing it
  ChannelEventSinkFactory.register();

  return Cc[SINK_CONTRACT_ID].getService(Ci.nsIChannelEventSink)
    .wrappedJSObject;
};
exports.ChannelEventSinkFactory = ChannelEventSinkFactory;