ve(subject, topic, data) {
    // Extract the wrapped object for subjects that are one of our wrappers
    // around a JS object.  This way we support both wrapped subjects created
    // using this module and those that are real XPCOM components.
    if (
      subject &&
      typeof subject == "object" &&
      "wrappedJSObject" in subject &&
      "observersModuleSubjectWrapper" in subject.wrappedJSObject
    ) {
      subject = subject.wrappedJSObject.object;
    }

    if (typeof this.callback == "function") {
      if (this.thisObject) {
        this.callback.call(this.thisObject, subject, data);
      } else {
        this.callback(subject, data);
      }
    } else {
      // typeof this.callback == "object" (nsIObserver)
      this.callback.observe(subject, topic, data);
    }
  },
};

function Subject(object) {
  // Double-wrap the object and set a property identifying the wrappedJSObject
  // as one of our wrappers to distinguish between subjects that are one of our
  // wrappers (which we should unwrap when notifying our observers) and those
  // that are real JS XPCOM components (which we should pass through unaltered).
  this.wrappedJSObject = { observersModuleSubjectWrapper: true, object };
}

Subject.prototype = {
  QueryInterface: ChromeUtils.generateQI([]),
  getScriptableHelper() {},
  getInterfaces() {},
};
PK