diff options
Diffstat (limited to 'toolkit/jetpack/sdk/event/dom.js')
-rw-r--r-- | toolkit/jetpack/sdk/event/dom.js | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/event/dom.js b/toolkit/jetpack/sdk/event/dom.js new file mode 100644 index 000000000..da99dec7a --- /dev/null +++ b/toolkit/jetpack/sdk/event/dom.js @@ -0,0 +1,78 @@ +/* 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"; + +module.metadata = { + "stability": "unstable" +}; + +const { Ci } = require("chrome"); + +var { emit } = require("./core"); +var { when: unload } = require("../system/unload"); +var listeners = new WeakMap(); + +const { Cu } = require("chrome"); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); +const { ThreadSafeChromeUtils } = Cu.import("resource://gre/modules/Services.jsm", {}); + +var getWindowFrom = x => + x instanceof Ci.nsIDOMWindow ? x : + x instanceof Ci.nsIDOMDocument ? x.defaultView : + x instanceof Ci.nsIDOMNode ? x.ownerDocument.defaultView : + null; + +function removeFromListeners() { + ShimWaiver.getProperty(this, "removeEventListener")("DOMWindowClose", removeFromListeners); + for (let cleaner of listeners.get(this)) + cleaner(); + + listeners.delete(this); +} + +// Simple utility function takes event target, event type and optional +// `options.capture` and returns node style event stream that emits "data" +// events every time event of that type occurs on the given `target`. +function open(target, type, options) { + let output = {}; + let capture = options && options.capture ? true : false; + let listener = (event) => emit(output, "data", event); + + // `open` is currently used only on DOM Window objects, however it was made + // to be used to any kind of `target` that supports `addEventListener`, + // therefore is safer get the `window` from the `target` instead assuming + // that `target` is the `window`. + let window = getWindowFrom(target); + + // If we're not able to get a `window` from `target`, there is something + // wrong. We cannot add listeners that can leak later, or results in + // "dead object" exception. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833 + if (!window) + throw new Error("Unable to obtain the owner window from the target given."); + + let cleaners = listeners.get(window); + if (!cleaners) { + cleaners = []; + listeners.set(window, cleaners); + + // We need to remove from our map the `window` once is closed, to prevent + // memory leak + ShimWaiver.getProperty(window, "addEventListener")("DOMWindowClose", removeFromListeners); + } + + cleaners.push(() => ShimWaiver.getProperty(target, "removeEventListener")(type, listener, capture)); + ShimWaiver.getProperty(target, "addEventListener")(type, listener, capture); + + return output; +} + +unload(() => { + let keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(listeners) + for (let window of keys) + removeFromListeners.call(window); +}); + +exports.open = open; |