Skip to content
This repository has been archived by the owner on Feb 26, 2022. It is now read-only.

Commit

Permalink
Bug 1196975 - part2: shimwaiver applications. r=mossop
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabor Krizsanits committed Oct 9, 2015
1 parent bb19063 commit 6961d88
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 128 deletions.
9 changes: 6 additions & 3 deletions lib/sdk/core/observer.js
Expand Up @@ -9,14 +9,17 @@ module.metadata = {
};


const { Cc, Ci, Cr } = require("chrome");
const { Cc, Ci, Cr, Cu } = require("chrome");
const { Class } = require("./heritage");
const { isWeak } = require("./reference");
const method = require("../../method/core");

const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const observerService = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);

const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");

// This is a method that will be invoked when notification observer
// subscribed to occurs.
Expand Down
18 changes: 18 additions & 0 deletions lib/sdk/dom/events-shimmed.js
@@ -0,0 +1,18 @@
/* 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 events = require('./events.js');

exports.emit = (element, type, obj) => events.emit(element, type, obj, true);
exports.on = (element, type, listener, capture) => events.on(element, type, listener, capture, true);
exports.once = (element, type, listener, capture) => events.once(element, type, listener, capture, true);
exports.removeListener = (element, type, listener, capture) => events.removeListener(element, type, listener, capture, true);
exports.removed = events.removed;
exports.when = (element, eventName, capture) => events.when(element, eventName, capture ? capture : false, true);
47 changes: 35 additions & 12 deletions lib/sdk/dom/events.js
Expand Up @@ -8,6 +8,9 @@ module.metadata = {
"stability": "unstable"
};

const { Cu } = require("chrome");
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");

// Utility function that returns copy of the given `text` with last character
// removed if it is `"s"`.
function singularify(text) {
Expand Down Expand Up @@ -44,10 +47,14 @@ function getInitializerName(category) {
* See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
* for a detailed explanation.
*/
function on(element, type, listener, capture) {
function on(element, type, listener, capture, shimmed = false) {
// `capture` defaults to `false`.
capture = capture || false;
element.addEventListener(type, listener, capture);
if (shimmed) {
element.addEventListener(type, listener, capture);
} else {
ShimWaiver.getProperty(element, "addEventListener")(type, listener, capture);
}
}
exports.on = on;

Expand All @@ -73,11 +80,11 @@ exports.on = on;
* See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
* for a detailed explanation.
*/
function once(element, type, listener, capture) {
function once(element, type, listener, capture, shimmed = false) {
on(element, type, function selfRemovableListener(event) {
removeListener(element, type, selfRemovableListener, capture);
removeListener(element, type, selfRemovableListener, capture, shimmed);
listener.apply(this, arguments);
}, capture);
}, capture, shimmed);
}
exports.once = once;

Expand All @@ -103,8 +110,12 @@ exports.once = once;
* See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
* for a detailed explanation.
*/
function removeListener(element, type, listener, capture) {
element.removeEventListener(type, listener, capture);
function removeListener(element, type, listener, capture, shimmed = false) {
if (shimmed) {
element.removeEventListener(type, listener, capture);
} else {
ShimWaiver.getProperty(element, "removeEventListener")(type, listener, capture);
}
}
exports.removeListener = removeListener;

Expand All @@ -128,13 +139,17 @@ exports.removeListener = removeListener;
* initializer after firs `type` argument.
* @see https://developer.mozilla.org/En/DOM/Document.createEvent
*/
function emit(element, type, { category, initializer, settings }) {
function emit(element, type, { category, initializer, settings }, shimmed = false) {
category = category || "UIEvents";
initializer = initializer || getInitializerName(category);
let document = element.ownerDocument;
let event = document.createEvent(category);
event[initializer].apply(event, [type].concat(settings));
element.dispatchEvent(event);
if (shimmed) {
element.dispatchEvent(event);
} else {
ShimWaiver.getProperty(element, "dispatchEvent")(event);
}
};
exports.emit = emit;

Expand All @@ -158,12 +173,20 @@ const removed = element => {
};
exports.removed = removed;

const when = (element, eventName, capture=false) => new Promise(resolve => {
const when = (element, eventName, capture=false, shimmed=false) => new Promise(resolve => {
const listener = event => {
element.removeEventListener(eventName, listener, capture);
if (shimmed) {
element.removeEventListener(eventName, listener, capture);
} else {
ShimWaiver.getProperty(element, "removeEventListener")(eventName, listener, capture);
}
resolve(event);
};

element.addEventListener(eventName, listener, capture);
if (shimmed) {
element.addEventListener(eventName, listener, capture);
} else {
ShimWaiver.getProperty(element, "addEventListener")(eventName, listener, capture);
}
});
exports.when = when;
11 changes: 8 additions & 3 deletions lib/sdk/event/chrome.js
Expand Up @@ -8,10 +8,15 @@ module.metadata = {
"stability": "unstable"
};

const { Cc, Ci, Cr } = require("chrome");
const { Cc, Ci, Cr, Cu } = require("chrome");
const { emit, on, off } = require("./core");
const { addObserver, removeObserver } = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
var observerService = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);

const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");

const { when: unload } = require("../system/unload");

// Simple class that can be used to instantiate event channel that
Expand Down
11 changes: 7 additions & 4 deletions lib/sdk/event/dom.js
Expand Up @@ -14,14 +14,17 @@ var { emit } = require("./core");
var { when: unload } = require("../system/unload");
var listeners = new Map();

const { Cu } = require("chrome");
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.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() {
this.removeEventListener("DOMWindowClose", removeFromListeners);
ShimWaiver.getProperty(this, "removeEventListener")("DOMWindowClose", removeFromListeners);
for (let cleaner of listeners.get(this))
cleaner();

Expand Down Expand Up @@ -56,11 +59,11 @@ function open(target, type, options) {

// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
ShimWaiver.getProperty(window, "addEventListener")("DOMWindowClose", removeFromListeners);
}

cleaners.push(() => target.removeEventListener(type, listener, capture));
target.addEventListener(type, listener, capture);
cleaners.push(() => ShimWaiver.getProperty(target, "removeEventListener")(type, listener, capture));
ShimWaiver.getProperty(target, "addEventListener")(type, listener, capture);

return output;
}
Expand Down
8 changes: 6 additions & 2 deletions lib/sdk/input/system.js
Expand Up @@ -9,8 +9,12 @@ const { once, off } = require("../event/core");
const { id: addonID } = require("../self");

const unloadMessage = require("@loader/unload");
const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const observerService = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");


const addonUnloadTopic = "sdk:loader:destroy";

Expand Down
3 changes: 2 additions & 1 deletion lib/sdk/keyboard/observer.js
Expand Up @@ -50,7 +50,8 @@ const Observer = Class({
* Keyboard event being emitted.
*/
handleEvent(event) {
emit(this, event.type, event, event.target.ownerDocument.defaultView);
emit(this, event.type, event, event.target.ownerDocument ? event.target.ownerDocument.defaultView
: undefined);
}
});

Expand Down
16 changes: 16 additions & 0 deletions lib/sdk/system/events-shimmed.js
@@ -0,0 +1,16 @@
/* 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 events = require('./events.js');

exports.emit = (type, event) => events.emit(type, event, true);
exports.on = (type, listener, strong) => events.on(type, listener, strong, true);
exports.once = (type, listener) => events.once(type, listener, true);
exports.off = (type, listener) => events.off(type, listener, true);
60 changes: 43 additions & 17 deletions lib/sdk/system/events.js
Expand Up @@ -12,8 +12,13 @@ const { Cc, Ci, Cu } = require('chrome');
const { Unknown } = require('../platform/xpcom');
const { Class } = require('../core/heritage');
const { ns } = require('../core/namespace');
const { addObserver, removeObserver, notifyObservers } =
const observerService =
Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
const { addObserver, removeObserver, notifyObservers } = observerService;
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
const addObserverNoShim = ShimWaiver.getProperty(observerService, "addObserver");
const removeObserverNoShim = ShimWaiver.getProperty(observerService, "removeObserver");
const notifyObserversNoShim = ShimWaiver.getProperty(observerService, "notifyObservers");
const unloadSubject = require('@loader/unload');

const Subject = Class({
Expand All @@ -33,7 +38,7 @@ const Subject = Class({
getInterfaces: function() {}
});

function emit(type, event) {
function emit(type, event, shimmed = false) {
// From bug 910599
// We must test to see if 'subject' or 'data' is a defined property
// of the event object, but also allow primitives to be passed in,
Expand All @@ -48,7 +53,11 @@ function emit(type, event) {
// All other types return themselves (and cast to strings/null
// via observer service)
event;
notifyObservers(subject, type, data);
if (shimmed) {
notifyObservers(subject, type, data);
} else {
notifyObserversNoShim(subject, type, data);
}
}
exports.emit = emit;

Expand Down Expand Up @@ -83,7 +92,7 @@ const Observer = Class({

const subscribers = ns();

function on(type, listener, strong) {
function on(type, listener, strong, shimmed = false) {
// Unless last optional argument is `true` we use a weak reference to a
// listener.
let weak = !strong;
Expand All @@ -94,38 +103,48 @@ function on(type, listener, strong) {
if (!(type in observers)) {
let observer = Observer(listener);
observers[type] = observer;
addObserver(observer, type, weak);
if (shimmed) {
addObserver(observer, type, weak);
} else {
addObserverNoShim(observer, type, weak);
}
// WeakRef gymnastics to remove all alive observers on unload
let ref = Cu.getWeakReference(observer);
weakRefs.set(observer, ref);
stillAlive.set(ref, type);
wasShimmed.set(ref, shimmed);
}
}
exports.on = on;

function once(type, listener) {
function once(type, listener, shimmed = false) {
// Note: this code assumes order in which listeners are called, which is fine
// as long as dispatch happens in same order as listener registration which
// is the case now. That being said we should be aware that this may break
// in a future if order will change.
on(type, listener);
on(type, listener, shimmed);
on(type, function cleanup() {
off(type, listener);
off(type, cleanup);
}, true);
off(type, listener, shimmed);
off(type, cleanup, shimmed);
}, true, shimmed);
}
exports.once = once;

function off(type, listener) {
function off(type, listener, shimmed = false) {
// Take list of observers as with the given `listener`.
let observers = subscribers(listener);
// If `observer` for the given `type` is registered, then
// remove it & unregister.
if (type in observers) {
let observer = observers[type];
delete observers[type];
removeObserver(observer, type);
if (shimmed) {
removeObserver(observer, type);
} else {
removeObserverNoShim(observer, type);
}
stillAlive.delete(weakRefs.get(observer));
wasShimmed.delete(weakRefs.get(observer));
}
}
exports.off = off;
Expand All @@ -134,22 +153,29 @@ exports.off = off;
var weakRefs = new WeakMap();

// and we're out of beta, we're releasing on time!
var stillAlive = new Map();
var stillAlive = new Map();

var wasShimmed = new Map();

on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
// using logic from ./unload, to avoid a circular module reference
if (subject.wrappedJSObject === unloadSubject) {
off('sdk:loader:destroy', onunload);
off('sdk:loader:destroy', onunload, false);

// don't bother
if (reason === 'shutdown')
return;

stillAlive.forEach( (type, ref) => {
let observer = ref.get();
if (observer)
removeObserver(observer, type);
if (observer) {
if (wasShimmed.get(ref)) {
removeObserver(observer, type);
} else {
removeObserverNoShim(observer, type);
}
}
})
}
// a strong reference
}, true);
}, true, false);
4 changes: 2 additions & 2 deletions mapping.json
Expand Up @@ -17,7 +17,7 @@
"preferences-service": "sdk/preferences/service",
"promise": "sdk/core/promise",
"system": "sdk/system",
"system/events": "sdk/system/events",
"system/events": "sdk/system/events-shimmed",
"tabs/tab": "sdk/tabs/tab",
"tabs/utils": "sdk/tabs/utils",
"timer": "sdk/timers",
Expand All @@ -40,7 +40,7 @@
"app-strings": "sdk/deprecated/app-strings",
"environment": "sdk/system/environment",
"keyboard/utils": "sdk/keyboard/utils",
"dom/events": "sdk/dom/events",
"dom/events": "sdk/dom/events-shimmed",
"utils/data": "sdk/io/data",
"test/assert": "sdk/test/assert",
"hidden-frame": "sdk/frame/hidden-frame",
Expand Down

0 comments on commit 6961d88

Please sign in to comment.