system.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. "use strict";
  5. const { Cc, Ci, Cr, Cu } = require("chrome");
  6. const { Input, start, stop, end, receive, outputs } = require("../event/utils");
  7. const { once, off } = require("../event/core");
  8. const { id: addonID } = require("../self");
  9. const unloadMessage = require("@loader/unload");
  10. const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
  11. getService(Ci.nsIObserverService);
  12. const addonUnloadTopic = "sdk:loader:destroy";
  13. const isXrayWrapper = Cu.isXrayWrapper;
  14. // In the past SDK used to double-wrap notifications dispatched, which
  15. // made them awkward to use outside of SDK. At present they no longer
  16. // do that, although we still supported for legacy reasons.
  17. const isLegacyWrapper = x =>
  18. x && x.wrappedJSObject &&
  19. "observersModuleSubjectWrapper" in x.wrappedJSObject;
  20. const unwrapLegacy = x => x.wrappedJSObject.object;
  21. // `InputPort` provides a way to create a signal out of the observer
  22. // notification subject's for the given `topic`. If `options.initial`
  23. // is provided it is used as initial value otherwise `null` is used.
  24. // Constructor can be given `options.id` that will be used to create
  25. // a `topic` which is namespaced to an add-on (this avoids conflicts
  26. // when multiple add-on are used, although in a future host probably
  27. // should just be shared across add-ons). It is also possible to
  28. // specify a specific `topic` via `options.topic` which is used as
  29. // without namespacing. Created signal ends whenever add-on is
  30. // unloaded.
  31. const InputPort = function InputPort({id, topic, initial}) {
  32. this.id = id || topic;
  33. this.topic = topic || "sdk:" + addonID + ":" + id;
  34. this.value = initial === void(0) ? null : initial;
  35. this.observing = false;
  36. this[outputs] = [];
  37. };
  38. // InputPort type implements `Input` signal interface.
  39. InputPort.prototype = new Input();
  40. InputPort.prototype.constructor = InputPort;
  41. // When port is started (which is when it's subgraph get's
  42. // first subscriber) actual observer is registered.
  43. InputPort.start = input => {
  44. input.addListener(input);
  45. // Also register add-on unload observer to end this signal
  46. // when that happens.
  47. addObserver(input, addonUnloadTopic, false);
  48. };
  49. InputPort.prototype[start] = InputPort.start;
  50. InputPort.addListener = input => addObserver(input, input.topic, false);
  51. InputPort.prototype.addListener = InputPort.addListener;
  52. // When port is stopped (which is when it's subgraph has no
  53. // no subcribers left) an actual observer unregistered.
  54. // Note that port stopped once it ends as well (which is when
  55. // add-on is unloaded).
  56. InputPort.stop = input => {
  57. input.removeListener(input);
  58. removeObserver(input, addonUnloadTopic);
  59. };
  60. InputPort.prototype[stop] = InputPort.stop;
  61. InputPort.removeListener = input => removeObserver(input, input.topic);
  62. InputPort.prototype.removeListener = InputPort.removeListener;
  63. // `InputPort` also implements `nsIObserver` interface and
  64. // `nsISupportsWeakReference` interfaces as it's going to be used as such.
  65. InputPort.prototype.QueryInterface = function(iid) {
  66. if (!iid.equals(Ci.nsIObserver) && !iid.equals(Ci.nsISupportsWeakReference))
  67. throw Cr.NS_ERROR_NO_INTERFACE;
  68. return this;
  69. };
  70. // `InputPort` instances implement `observe` method, which is invoked when
  71. // observer notifications are dispatched. The `subject` of that notification
  72. // are received on this signal.
  73. InputPort.prototype.observe = function(subject, topic, data) {
  74. // Unwrap message from the subject. SDK used to have it's own version of
  75. // wrappedJSObjects which take precedence, if subject has `wrappedJSObject`
  76. // and it's not an XrayWrapper use it as message. Otherwise use subject as
  77. // is.
  78. const message = subject === null ? null :
  79. isLegacyWrapper(subject) ? unwrapLegacy(subject) :
  80. isXrayWrapper(subject) ? subject :
  81. subject.wrappedJSObject ? subject.wrappedJSObject :
  82. subject;
  83. // If observer topic matches topic of the input port receive a message.
  84. if (topic === this.topic) {
  85. receive(this, message);
  86. }
  87. // If observe topic is add-on unload topic we create an end message.
  88. if (topic === addonUnloadTopic && message === unloadMessage) {
  89. end(this);
  90. }
  91. };
  92. exports.InputPort = InputPort;