Bug 1196975 - part2: shimwaiver applications. r=mossop
authorGabor Krizsanits <gkrizsanits>
Fri, 09 Oct 2015 06:00:00 +0200
changeset 293022 a4e71f016f2726fbd1aec892774c57f1577553f4
parent 293021 44f249ecb0cd0075169607758e484df13fe5304d
child 293023 088a84f0a1ad2e89bf147412f550c5fc9d463ac2
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop
bugs1196975
milestone45.0a1
Bug 1196975 - part2: shimwaiver applications. r=mossop
addon-sdk/moz.build
addon-sdk/source/lib/sdk/core/observer.js
addon-sdk/source/lib/sdk/dom/events-shimmed.js
addon-sdk/source/lib/sdk/dom/events.js
addon-sdk/source/lib/sdk/event/chrome.js
addon-sdk/source/lib/sdk/event/dom.js
addon-sdk/source/lib/sdk/input/system.js
addon-sdk/source/lib/sdk/keyboard/observer.js
addon-sdk/source/lib/sdk/system/events-shimmed.js
addon-sdk/source/lib/sdk/system/events.js
addon-sdk/source/mapping.json
addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
addon-sdk/source/test/test-content-events.js
addon-sdk/source/test/test-dev-panel.js
addon-sdk/source/test/util.js
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -277,16 +277,17 @@ EXTRA_JS_MODULES.commonjs.sdk.core += [
     'source/lib/sdk/core/reference.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.deprecated.events += [
     'source/lib/sdk/deprecated/events/assembler.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.dom += [
+    'source/lib/sdk/dom/events-shimmed.js',
     'source/lib/sdk/dom/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.dom.events += [
     'source/lib/sdk/dom/events/keys.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.event += [
@@ -403,16 +404,17 @@ EXTRA_JS_MODULES.commonjs.sdk.remote += 
 EXTRA_JS_MODULES.commonjs.sdk.stylesheet += [
     'source/lib/sdk/stylesheet/style.js',
     'source/lib/sdk/stylesheet/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.system += [
     'source/lib/sdk/system/child_process.js',
     'source/lib/sdk/system/environment.js',
+    'source/lib/sdk/system/events-shimmed.js',
     'source/lib/sdk/system/events.js',
     'source/lib/sdk/system/globals.js',
     'source/lib/sdk/system/process.js',
     'source/lib/sdk/system/runtime.js',
     'source/lib/sdk/system/unload.js',
     'source/lib/sdk/system/xul-app.js',
     'source/lib/sdk/system/xul-app.jsm',
 ]
--- a/addon-sdk/source/lib/sdk/core/observer.js
+++ b/addon-sdk/source/lib/sdk/core/observer.js
@@ -4,24 +4,27 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
-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.
 const observe = method("observer/observe");
 exports.observe = observe;
 
 // Method to subscribe to the observer notification.
 const subscribe = method("observe/subscribe");
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/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);
--- a/addon-sdk/source/lib/sdk/dom/events.js
+++ b/addon-sdk/source/lib/sdk/dom/events.js
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 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) {
   return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text;
 }
 
 // Utility function that takes event type, argument is passed to
 // `document.createEvent` and returns name of the initializer method of the
@@ -39,20 +42,24 @@ function getInitializerName(category) {
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    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;
 
 /**
  * Registers an event `listener` on a given `element`, that will be called
  * only once, next time event of specified `type` is dispatched on the
  * `element`.
  * @param {Element} element
@@ -68,21 +75,21 @@ exports.on = on;
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    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;
 
 /**
  * Unregisters an event `listener` on a given `element` for the events of the
  * specified `type`.
  *
  * @param {Element} element
@@ -98,18 +105,22 @@ exports.once = once;
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    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;
 
 /**
  * Emits event of the specified `type` and `category` on the given `element`.
  * Specified `settings` are used to initialize event before dispatching it.
  * @param {Element} element
  *    Dom element to dispatch event on.
@@ -123,23 +134,27 @@ exports.removeListener = removeListener;
  *    - `initializer`: If passed it will be used as name of the method used
  *      to initialize event. If omitted name will be generated from the
  *      `category` field by prefixing it with `"init"` and removing last
  *      character if it matches `"s"`.
  *    - `settings`: Array of settings that are forwarded to the event
  *      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;
 
 // Takes DOM `element` and returns promise which is resolved
 // when given element is removed from it's parent node.
 const removed = element => {
   return new Promise(resolve => {
     const { MutationObserver } = element.ownerDocument.defaultView;
@@ -153,17 +168,25 @@ const removed = element => {
         }
       }
     });
     observer.observe(element.parentNode, {childList: true});
   });
 };
 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;
--- a/addon-sdk/source/lib/sdk/event/chrome.js
+++ b/addon-sdk/source/lib/sdk/event/chrome.js
@@ -3,20 +3,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 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
 // implements `nsIObserver` interface. It's will is used by `observe`
 // function as observer + event target. It basically proxies observer
 // notifications as to it's registered listeners.
 function ObserverChannel() {}
 Object.freeze(Object.defineProperties(ObserverChannel.prototype, {
--- a/addon-sdk/source/lib/sdk/event/dom.js
+++ b/addon-sdk/source/lib/sdk/event/dom.js
@@ -9,24 +9,27 @@ module.metadata = {
 };
 
 const { Ci } = require("chrome");
 
 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();
 
   listeners.delete(this);
 }
 
 // Simple utility function takes event target, event type and optional
 // `options.capture` and returns node style event stream that emits "data"
@@ -51,21 +54,21 @@ function open(target, type, options) {
 
   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
-    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;
 }
 
 unload(() => {
   for (let window of listeners.keys())
     removeFromListeners.call(window);
 });
--- a/addon-sdk/source/lib/sdk/input/system.js
+++ b/addon-sdk/source/lib/sdk/input/system.js
@@ -4,18 +4,22 @@
 "use strict";
 
 const { Cc, Ci, Cr, Cu } = require("chrome");
 const { Input, start, stop, end, receive, outputs } = require("../event/utils");
 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";
 
 const isXrayWrapper = Cu.isXrayWrapper;
 // In the past SDK used to double-wrap notifications dispatched, which
 // made them awkward to use outside of SDK. At present they no longer
 // do that, although we still supported for legacy reasons.
 const isLegacyWrapper = x =>
--- a/addon-sdk/source/lib/sdk/keyboard/observer.js
+++ b/addon-sdk/source/lib/sdk/keyboard/observer.js
@@ -45,13 +45,14 @@ const Observer = Class({
   /**
    * Function handles all the supported events on all the windows that are
    * observed. Method is used to proxy events to the listeners registered on
    * this event emitter.
    * @param {Event} event
    *    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);
   }
 });
 
 exports.observer = new Observer();
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/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);
--- a/addon-sdk/source/lib/sdk/system/events.js
+++ b/addon-sdk/source/lib/sdk/system/events.js
@@ -7,18 +7,23 @@
 module.metadata = {
   'stability': 'unstable'
 };
 
 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({
   extends: Unknown,
   initialize: function initialize(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
@@ -28,32 +33,36 @@ const Subject = Class({
       observersModuleSubjectWrapper: true,
       object: object
     };
   },
   getScriptableHelper: function() {},
   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,
   // which the `in` operator breaks, yet `null` is an object, hence
   // the long conditional
   let subject = event && typeof event === 'object' && 'subject' in event ?
     Subject(event.subject) :
     null;
   let data = event && typeof event === 'object' ?
     // An object either returns its `data` property or null
     ('data' in event ? event.data : null) :
     // 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;
 
 const Observer = Class({
   extends: Unknown,
   initialize: function initialize(listener) {
     this.listener = listener;
   },
@@ -78,78 +87,95 @@ const Observer = Class({
     catch (error) {
       console.exception(error);
     }
   }
 });
 
 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;
   // Take list of observers associated with given `listener` function.
   let observers = subscribers(listener);
   // If `observer` for the given `type` is not registered yet, then
   // associate an `observer` and register it.
   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;
 
 // must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115
 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);
--- a/addon-sdk/source/mapping.json
+++ b/addon-sdk/source/mapping.json
@@ -12,17 +12,17 @@
   "l10n/locale": "sdk/l10n/locale",
   "l10n/prefs": "sdk/l10n/prefs",
   "list": "sdk/util/list",
   "loader": "sdk/loader/loader",
   "namespace": "sdk/core/namespace",
   "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",
   "traits": "sdk/deprecated/traits",
   "unload": "sdk/system/unload",
   "window-utils": "sdk/deprecated/window-utils",
   "window/utils": "sdk/window/utils",
   "windows/dom": "sdk/windows/dom",
@@ -35,17 +35,17 @@
   "file": "sdk/io/file",
   "runtime": "sdk/system/runtime",
   "xpcom": "sdk/platform/xpcom",
   "querystring": "sdk/querystring",
   "text-streams": "sdk/io/text-streams",
   "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",
   "collection": "sdk/util/collection",
   "array": "sdk/util/array",
   "clipboard": "sdk/clipboard",
   "context-menu": "sdk/context-menu",
   "hotkeys": "sdk/hotkeys",
--- a/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
+++ b/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
@@ -137,17 +137,17 @@ exports["test compatibility"] = function
 
   assert.equal(require("querystring"),
                require("sdk/querystring"), "sdk/querystring -> querystring");
 
   assert.equal(require("tabs/utils"),
                require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
 
   assert.equal(require("dom/events"),
-               require("sdk/dom/events"), "sdk/dom/events -> dom/events");
+               require("sdk/dom/events-shimmed"), "sdk/dom/events-shimmed -> dom/events");
 
   assert.equal(require("tabs/tab.js"),
                require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
 
   assert.equal(require("environment"),
                require("sdk/system/environment"), "sdk/system/environment -> environment");
 
   assert.equal(require("test/assert"),
--- a/addon-sdk/source/test/test-content-events.js
+++ b/addon-sdk/source/test/test-content-events.js
@@ -37,96 +37,16 @@ var use = use = value => () => value;
 
 var open = curry((url, window) => openTab(window, url));
 var close = function(tab) {
   let promise = when("pagehide", tab);
   closeTab(tab);
   return promise;
 }
 
-exports["test multiple tabs"] = function(assert, done) {
-  let loader = Loader(module);
-  let { events } = loader.require("sdk/content/events");
-  let { on, off } = loader.require("sdk/event/core");
-  let actual = [];
-
-  on(events, "data", handler);
-  function handler ({type, target, timeStamp}) {
-    eventFilter(type, target, () => {
-      actual.push(type + " -> " + target.URL)
-    });
-  }
-
-  let window = getMostRecentBrowserWindow();
-  let firstTab = open("data:text/html,first-tab", window);
-
-  when("pageshow", firstTab).
-    then(close).
-    then(use(window)).
-    then(open("data:text/html,second-tab")).
-    then(when("pageshow")).
-    then(close).
-    then(function() {
-      assert.deepEqual(actual, [
-        "document-element-inserted -> data:text/html,first-tab",
-        "DOMContentLoaded -> data:text/html,first-tab",
-        "load -> data:text/html,first-tab",
-        "pageshow -> data:text/html,first-tab",
-        "document-element-inserted -> data:text/html,second-tab",
-        "DOMContentLoaded -> data:text/html,second-tab",
-        "load -> data:text/html,second-tab",
-        "pageshow -> data:text/html,second-tab"
-      ], "all events dispatche as expeced")
-    }, function(reason) {
-      assert.fail(Error(reason));
-    }).then(function() {
-      loader.unload();
-      off(events, "data", handler);
-      done();
-    });
-};
-
-exports["test nested frames"] = function(assert, done) {
-  let loader = Loader(module);
-  let { events } = loader.require("sdk/content/events");
-  let { on, off } = loader.require("sdk/event/core");
-  let actual = [];
-  on(events, "data", handler);
-  function handler ({type, target, timeStamp}) {
-    eventFilter(type, target, () => {
-      actual.push(type + " -> " + target.URL)
-    });
-  }
-
-  let window =  getMostRecentBrowserWindow();
-  let uri = encodeURI("data:text/html,<iframe src='data:text/html,iframe'>");
-  let tab = open(uri, window);
-
-  when("pageshow", tab).
-    then(close).
-    then(function() {
-      assert.deepEqual(actual, [
-        "document-element-inserted -> " + uri,
-        "DOMContentLoaded -> " + uri,
-        "document-element-inserted -> data:text/html,iframe",
-        "DOMContentLoaded -> data:text/html,iframe",
-        "load -> data:text/html,iframe",
-        "pageshow -> data:text/html,iframe",
-        "load -> " + uri,
-        "pageshow -> " + uri
-      ], "events where dispatched")
-    }, function(reason) {
-      assert.fail(Error(reason))
-    }).then(function() {
-      loader.unload();
-      off(events, "data", handler);
-      done();
-    });
-};
-
 exports["test dead object errors"] = function(assert, done) {
   let system = require("sdk/system/events");
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
 
   // The dead object error is properly reported on console but
   // doesn't raise any test's exception
   function onMessage({ subject }) {
--- a/addon-sdk/source/test/test-dev-panel.js
+++ b/addon-sdk/source/test/test-dev-panel.js
@@ -9,17 +9,17 @@ module.metadata = {
   }
 };
 
 const { Tool } = require("dev/toolbox");
 const { Panel } = require("dev/panel");
 const { Class } = require("sdk/core/heritage");
 const { openToolbox, closeToolbox, getCurrentPanel } = require("dev/utils");
 const { MessageChannel } = require("sdk/messaging");
-const { when } = require("sdk/dom/events");
+const { when } = require("sdk/dom/events-shimmed");
 const { viewFor } = require("sdk/view/core");
 const { createView } = require("dev/panel/view");
 
 const iconURI = "data:image/png;base64,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TUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6M0ExNEY4NjZBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0YW5jZUlEPnhtcC5paWQ6M0ExNEY4NjNBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3N0UmVmOmluc3RhbmNlSUQ+CiAgICAgICAgICAgIDxzdFJlZjpkb2N1bWVudElEPnhtcC5kaWQ6M0ExNEY4NjRBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3N0UmVmOmRvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6M0ExNEY4NjVBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2g8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ChetDDYAAACaSURBVEgNY2AYboAR3UNnzpx5DxQTQBP/YGJiIogmBuYSq54Ji2Z0S0BKsInBtGKTwxDDZhHMAKrSdLOIqq7GZxjj+fPnBf7+/bseqMgBn0IK5A4wMzMHMtHYEpD7HEB2gOLIAcSjMXCgW2IYtYjsqBwNutGgg4fAaGKABwWpDLoG3QFSXUeG+gNMoEoJqJGWloErPjIcR54WALqPHeiJgl15AAAAAElFTkSuQmCC";
 const makeHTML = fn =>
   "data:text/html;charset=utf-8,<script>(" + fn + ")();</script>";
 
 
--- a/addon-sdk/source/test/util.js
+++ b/addon-sdk/source/test/util.js
@@ -7,26 +7,30 @@ const {Cc, Ci} = require("chrome");
 const {getMostRecentBrowserWindow, open} = require("sdk/window/utils");
 const tabUtils = require("sdk/tabs/utils");
 const {when} = require("sdk/dom/events");
 
 
 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 getActiveTab = (window=getMostRecentBrowserWindow()) =>
   tabUtils.getActiveTab(window)
 
 const openWindow = () => {
   const window = open();
   return new Promise((resolve) => {
-    observerService.addObserver({
+    addObserver({
       observe(subject, topic) {
         if (subject === window) {
-          observerService.removeObserver(this, topic);
+          removeObserver(this, topic);
           resolve(subject);
         }
       }
     }, "browser-delayed-startup-finished", false);
   });
 };
 exports.openWindow = openWindow;