tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

server-sent-events.js (3872B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2      +  * License, v. 2.0. If a copy of the MPL was not distributed with this
      3      +  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  LongStringActor,
      9 } = require("resource://devtools/server/actors/string.js");
     10 
     11 const eventSourceEventService = Cc[
     12  "@mozilla.org/eventsourceevent/service;1"
     13 ].getService(Ci.nsIEventSourceEventService);
     14 
     15 class ServerSentEventWatcher {
     16  constructor() {
     17    this.windowIds = new Set();
     18    // Register for backend events.
     19    this.onWindowReady = this.onWindowReady.bind(this);
     20    this.onWindowDestroy = this.onWindowDestroy.bind(this);
     21  }
     22  /**
     23   * Start watching for all server sent events related to a given Target Actor.
     24   *
     25   * @param TargetActor targetActor
     26   *        The target actor on which we should observe server sent events.
     27   * @param Object options
     28   *        Dictionary object with following attributes:
     29   *        - onAvailable: mandatory function
     30   *          This will be called for each resource.
     31   */
     32  watch(targetActor, { onAvailable }) {
     33    this.onAvailable = onAvailable;
     34    this.targetActor = targetActor;
     35 
     36    for (const window of this.targetActor.windows) {
     37      const { innerWindowId } = window.windowGlobalChild;
     38      this.startListening(innerWindowId);
     39    }
     40 
     41    // Listen for subsequent top-level-document reloads/navigations,
     42    // new iframe additions or current iframe reloads/navigation.
     43    this.targetActor.on("window-ready", this.onWindowReady);
     44    this.targetActor.on("window-destroyed", this.onWindowDestroy);
     45  }
     46 
     47  static createResource(messageType, eventParams) {
     48    return {
     49      messageType,
     50      ...eventParams,
     51    };
     52  }
     53 
     54  static prepareFramePayload(targetActor, frame) {
     55    const payload = new LongStringActor(targetActor.conn, frame);
     56    targetActor.manage(payload);
     57    return payload.form();
     58  }
     59 
     60  onWindowReady({ window }) {
     61    const { innerWindowId } = window.windowGlobalChild;
     62    this.startListening(innerWindowId);
     63  }
     64 
     65  onWindowDestroy({ id }) {
     66    this.stopListening(id);
     67  }
     68 
     69  startListening(innerWindowId) {
     70    if (!this.windowIds.has(innerWindowId)) {
     71      this.windowIds.add(innerWindowId);
     72      eventSourceEventService.addListener(innerWindowId, this);
     73    }
     74  }
     75 
     76  stopListening(innerWindowId) {
     77    if (this.windowIds.has(innerWindowId)) {
     78      this.windowIds.delete(innerWindowId);
     79      // The listener might have already been cleaned up on `window-destroy`.
     80      if (!eventSourceEventService.hasListenerFor(innerWindowId)) {
     81        console.warn(
     82          "Already stopped listening to server sent events for this window."
     83        );
     84        return;
     85      }
     86      eventSourceEventService.removeListener(innerWindowId, this);
     87    }
     88  }
     89 
     90  destroy() {
     91    // cleanup any other listeners not removed on `window-destroy`
     92    for (const id of this.windowIds) {
     93      this.stopListening(id);
     94    }
     95    this.targetActor.off("window-ready", this.onWindowReady);
     96    this.targetActor.off("window-destroyed", this.onWindowDestroy);
     97  }
     98 
     99  // nsIEventSourceEventService specific functions
    100  eventSourceConnectionOpened() {}
    101 
    102  eventSourceConnectionClosed(httpChannelId) {
    103    const resource = ServerSentEventWatcher.createResource(
    104      "eventSourceConnectionClosed",
    105      { httpChannelId }
    106    );
    107    this.onAvailable([resource]);
    108  }
    109 
    110  eventReceived(httpChannelId, eventName, lastEventId, data, retry, timeStamp) {
    111    const payload = ServerSentEventWatcher.prepareFramePayload(
    112      this.targetActor,
    113      data
    114    );
    115    const resource = ServerSentEventWatcher.createResource("eventReceived", {
    116      httpChannelId,
    117      data: {
    118        payload,
    119        eventName,
    120        lastEventId,
    121        retry,
    122        timeStamp,
    123      },
    124    });
    125 
    126    this.onAvailable([resource]);
    127  }
    128 }
    129 
    130 module.exports = ServerSentEventWatcher;