Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 01 Oct 2012 21:24:30 -0400
changeset 108902 fb076a446870dcf4b7d4b79ef03e98137ca02a2d
parent 108841 63b393c1facc01f150bc3c1cd40e6132a537260b (current diff)
parent 108901 3bc2919782a70d5de289afdc3958670900e77fd7 (diff)
child 108903 ddd4e4a5f33795ebe6e036ed39080280cef9156a
child 108960 932204a65b9c0edb9dfd940172280890423d7926
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge the last PGO-green inbound changeset to m-c.
accessible/src/jsat/VirtualCursorController.jsm
gfx/2d/basictypes.h
gfx/2d/convolver.h
gfx/2d/cpu.h
gfx/2d/image_operations.cpp
gfx/2d/port.h
gfx/2d/stack_container.h
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -7,486 +7,477 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ['AccessFu'];
 
 Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/Geometry.jsm');
 
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
-Cu.import('resource://gre/modules/accessibility/Presenters.jsm');
-Cu.import('resource://gre/modules/accessibility/VirtualCursorController.jsm');
 Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
 
 const ACCESSFU_DISABLE = 0;
 const ACCESSFU_ENABLE = 1;
 const ACCESSFU_AUTO = 2;
 
 var AccessFu = {
   /**
-   * Attach chrome-layer accessibility functionality to the given chrome window.
-   * If accessibility is enabled on the platform (currently Android-only), then
-   * a special accessibility mode is started (see startup()).
-   * @param {ChromeWindow} aWindow Chrome window to attach to.
-   * @param {boolean} aForceEnabled Skip platform accessibility check and enable
-   *  AccessFu.
+   * Initialize chrome-layer accessibility functionality.
+   * If accessibility is enabled on the platform, then a special accessibility
+   * mode is started.
    */
   attach: function attach(aWindow) {
     if (this.chromeWin)
       // XXX: only supports attaching to one window now.
       throw new Error('Only one window could be attached to AccessFu');
 
     Logger.info('attach');
     this.chromeWin = aWindow;
-    this.presenters = [];
 
     this.prefsBranch = Cc['@mozilla.org/preferences-service;1']
       .getService(Ci.nsIPrefService).getBranch('accessibility.accessfu.');
     this.prefsBranch.addObserver('activate', this, false);
-    this.prefsBranch.addObserver('explorebytouch', this, false);
 
     this.touchAdapter = TouchAdapter;
 
-    switch(Utils.MozBuildApp) {
+    switch (Utils.MozBuildApp) {
       case 'mobile/android':
         Services.obs.addObserver(this, 'Accessibility:Settings', false);
-        Services.obs.addObserver(this, 'Accessibility:NextObject', false);
-        Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
-        Services.obs.addObserver(this, 'Accessibility:CurrentObject', false);
+        Cc['@mozilla.org/android/bridge;1'].
+          getService(Ci.nsIAndroidBridge).handleGeckoMessage(
+            JSON.stringify({ gecko: { type: 'Accessibility:Ready' } }));
         this.touchAdapter = AndroidTouchAdapter;
         break;
       case 'b2g':
         aWindow.addEventListener(
           'ContentStart',
           (function(event) {
              let content = aWindow.shell.contentBrowser.contentWindow;
              content.addEventListener('mozContentEvent', this, false, true);
            }).bind(this), false);
         break;
       default:
         break;
     }
 
-    this._processPreferences();
+    try {
+      this._activatePref = this.prefsBranch.getIntPref('activate');
+    } catch (x) {
+      this._activatePref = ACCESSFU_DISABLE;
+    }
+
+    this._enableOrDisable();
   },
 
   /**
    * Start AccessFu mode, this primarily means controlling the virtual cursor
    * with arrow keys.
    */
   _enable: function _enable() {
     if (this._enabled)
       return;
     this._enabled = true;
 
     Logger.info('enable');
 
+    for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
+      this._loadFrameScript(mm);
+
     // Add stylesheet
     let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
     this.stylesheet = this.chromeWin.document.createProcessingInstruction(
       'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
-    this.chromeWin.document.insertBefore(this.stylesheet, this.chromeWin.document.firstChild);
-
-    this.addPresenter(new VisualPresenter());
+    this.chromeWin.document.insertBefore(this.stylesheet,
+                                         this.chromeWin.document.firstChild);
 
-    // Implicitly add the Android presenter on Android.
-    if (Utils.MozBuildApp == 'mobile/android') {
-      this._androidPresenter = new AndroidPresenter();
-      this.addPresenter(this._androidPresenter);
-    } else if (Utils.MozBuildApp == 'b2g') {
-      this.addPresenter(new SpeechPresenter());
-    }
+    Input.attach(this.chromeWin);
+    Output.attach(this.chromeWin);
+    this.touchAdapter.attach(this.chromeWin);
 
-    VirtualCursorController.attach(this.chromeWin);
-
-    Services.obs.addObserver(this, 'accessible-event', false);
-    this.chromeWin.addEventListener('DOMActivate', this, true);
-    this.chromeWin.addEventListener('resize', this, true);
-    this.chromeWin.addEventListener('scroll', this, true);
-    this.chromeWin.addEventListener('TabOpen', this, true);
-    this.chromeWin.addEventListener('focus', this, true);
+    Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
+    Services.obs.addObserver(this, 'Accessibility:NextObject', false);
+    Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
+    Services.obs.addObserver(this, 'Accessibility:CurrentObject', false);
   },
 
   /**
    * Disable AccessFu and return to default interaction mode.
    */
   _disable: function _disable() {
     if (!this._enabled)
       return;
+
     this._enabled = false;
 
     Logger.info('disable');
 
     this.chromeWin.document.removeChild(this.stylesheet);
+    for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
+      mm.sendAsyncMessage('AccessFu:Stop');
 
-    this.presenters.forEach(function(p) { p.detach(); });
-    this.presenters = [];
-
-    VirtualCursorController.detach();
+    Input.detach();
 
-    Services.obs.removeObserver(this, 'accessible-event');
-    this.chromeWin.removeEventListener('DOMActivate', this, true);
-    this.chromeWin.removeEventListener('resize', this, true);
-    this.chromeWin.removeEventListener('scroll', this, true);
-    this.chromeWin.removeEventListener('TabOpen', this, true);
-    this.chromeWin.removeEventListener('focus', this, true);
+    Services.obs.removeObserver(this, 'remote-browser-frame-shown');
+    Services.obs.removeObserver(this, 'Accessibility:NextObject');
+    Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
+    Services.obs.removeObserver(this, 'Accessibility:CurrentObject');
   },
 
-  _processPreferences: function _processPreferences(aEnabled, aTouchEnabled) {
-    let accessPref = ACCESSFU_DISABLE;
+  _enableOrDisable: function _enableOrDisable() {
     try {
-      accessPref = (aEnabled == undefined) ?
-        this.prefsBranch.getIntPref('activate') : aEnabled;
+      if (this._activatePref == ACCESSFU_ENABLE ||
+          this._systemPref && this._activatePref == ACCESSFU_AUTO)
+        this._enable();
+      else
+        this._disable();
     } catch (x) {
-    }
-
-    let ebtPref = ACCESSFU_DISABLE;
-    try {
-      ebtPref = (aTouchEnabled == undefined) ?
-        this.prefsBranch.getIntPref('explorebytouch') : aTouchEnabled;
-    } catch (x) {
+      Logger.error(x);
     }
-
-    if (Utils.MozBuildApp == 'mobile/android') {
-      if (accessPref == ACCESSFU_AUTO) {
-        Cc['@mozilla.org/android/bridge;1'].
-          getService(Ci.nsIAndroidBridge).handleGeckoMessage(
-            JSON.stringify({ gecko: { type: 'Accessibility:Ready' } }));
-        return;
-      }
-    }
-
-    if (accessPref == ACCESSFU_ENABLE)
-      this._enable();
-    else
-      this._disable();
-
-    if (ebtPref == ACCESSFU_ENABLE)
-      this.touchAdapter.attach(this.chromeWin);
-    else
-      this.touchAdapter.detach(this.chromeWin);
-  },
-
-  addPresenter: function addPresenter(presenter) {
-    this.presenters.push(presenter);
-    presenter.attach(this.chromeWin);
   },
 
-  handleEvent: function handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case 'focus':
-      {
-        if (aEvent.target instanceof Ci.nsIDOMWindow) {
-          let docAcc = getAccessible(aEvent.target.document);
-          let docContext = new PresenterContext(docAcc, null);
-          let cursorable = docAcc.QueryInterface(Ci.nsIAccessibleCursorable);
-          let vcContext = new PresenterContext(
-            (cursorable) ? cursorable.virtualCursor.position : null, null);
-          this.presenters.forEach(
-            function(p) { p.tabSelected(docContext, vcContext); });
+  receiveMessage: function receiveMessage(aMessage) {
+    if (Logger.logLevel >= Logger.DEBUG)
+      Logger.debug('Recieved', aMessage.name, JSON.stringify(aMessage.json));
+
+    switch (aMessage.name) {
+      case 'AccessFu:Ready':
+      let mm = Utils.getMessageManager(aMessage.target);
+      mm.sendAsyncMessage('AccessFu:Start',
+                          {method: 'start', buildApp: Utils.MozBuildApp});
+      break;
+      case 'AccessFu:Present':
+      try {
+        for each (let presenter in aMessage.json) {
+          Output[presenter.type](presenter.details, aMessage.target);
+        }
+      } catch (x) {
+        Logger.error(x);
+      }
+      break;
+      case 'AccessFu:Input':
+      Input.setEditState(aMessage.json);
+      break;
+    }
+  },
+
+  _loadFrameScript: function _loadFrameScript(aMessageManager) {
+    aMessageManager.addMessageListener('AccessFu:Present', this);
+    aMessageManager.addMessageListener('AccessFu:Input', this);
+    aMessageManager.addMessageListener('AccessFu:Ready', this);
+    aMessageManager.
+      loadFrameScript(
+        'chrome://global/content/accessibility/content-script.js', true);
+  },
+
+  observe: function observe(aSubject, aTopic, aData) {
+    Logger.debug('observe', aTopic);
+    switch (aTopic) {
+      case 'Accessibility:Settings':
+        this._systemPref = JSON.parse(aData).enabled;
+        this._enableOrDisable();
+        break;
+      case 'Accessibility:NextObject':
+        Input.moveCursor('moveNext', 'Simple', 'gesture');
+        break;
+      case 'Accessibility:PreviousObject':
+        Input.moveCursor('movePrevious', 'Simple', 'gesture');
+        break;
+      case 'Accessibility:CurrentObject':
+        let mm = Utils.getCurrentBrowser(this.chromeWin).
+          frameLoader.messageManager;
+        mm.sendAsyncMessage('AccessFu:VirtualCursor',
+                            {action: 'presentLastPivot'});
+        break;
+      case 'nsPref:changed':
+        if (aData == 'activate') {
+          this._activatePref = this.prefsBranch.getIntPref('activate');
+          this._enableOrDisable();
         }
         break;
-      }
-      case 'TabOpen':
-      {
-        let browser = aEvent.target.linkedBrowser || aEvent.target;
-        // Store the new browser node. We will need to check later when a new
-        // content document is attached if it has been attached to this new tab.
-        // If it has, than we will need to send a 'loading' message along with
-        // the usual 'newdoc' to presenters.
-        this._pendingDocuments[browser] = true;
-        this.presenters.forEach(
-          function(p) {
-            p.tabStateChanged(null, 'newtab');
-          }
-        );
-        break;
-      }
-      case 'DOMActivate':
+      case 'remote-browser-frame-shown':
       {
-        let activatedAcc = getAccessible(aEvent.originalTarget);
-        let state = {};
-        activatedAcc.getState(state, {});
-
-        // Checkable objects will have a state changed event that we will use
-        // instead of this hackish DOMActivate. We will also know the true
-        // action that was taken.
-        if (state.value & Ci.nsIAccessibleStates.STATE_CHECKABLE)
-          return;
-
-        this.presenters.forEach(function(p) {
-                                  p.actionInvoked(activatedAcc, 'click');
-                                });
-        break;
-      }
-      case 'scroll':
-      case 'resize':
-      {
-        this.presenters.forEach(function(p) { p.viewportChanged(); });
-        break;
-      }
-      case 'mozContentEvent':
-      {
-        if (aEvent.detail.type == 'accessibility-screenreader') {
-          let pref = aEvent.detail.enabled + 0;
-          this._processPreferences(pref, pref);
-        }
+        this._loadFrameScript(
+          aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager);
         break;
       }
     }
   },
 
-  observe: function observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case 'Accessibility:Settings':
-        this._processPreferences(JSON.parse(aData).enabled + 0,
-                                 JSON.parse(aData).exploreByTouch + 0);
-        break;
-      case 'Accessibility:NextObject':
-        VirtualCursorController.
-          moveForward(Utils.getCurrentContentDoc(this.chromeWin));
-        break;
-      case 'Accessibility:PreviousObject':
-        VirtualCursorController.
-          moveBackward(Utils.getCurrentContentDoc(this.chromeWin));
-        break;
-      case 'Accessibility:CurrentObject':
-        this._androidPresenter.accessibilityFocus();
-        break;
-      case 'nsPref:changed':
-        this._processPreferences(this.prefsBranch.getIntPref('activate'),
-                                 this.prefsBranch.getIntPref('explorebytouch'));
-        break;
-      case 'accessible-event':
-        let event;
-        try {
-          event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
-          this._handleAccEvent(event);
-        } catch (ex) {
-          Logger.error(ex);
-          return;
-        }
+  handleEvent: function handleEvent(aEvent) {
+    if (aEvent.type == 'mozContentEvent' &&
+        aEvent.detail.type == 'accessibility-screenreader') {
+      this._systemPref = aEvent.detail.enabled;
+      this._enableOrDisable();
     }
   },
 
-  _handleAccEvent: function _handleAccEvent(aEvent) {
-    if (Logger.logLevel <= Logger.DEBUG)
-      Logger.debug(Logger.eventToString(aEvent),
-                   Logger.accessibleToString(aEvent.accessible));
-
-    switch (aEvent.eventType) {
-      case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
-        {
-          let pivot = aEvent.accessible.
-            QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
-          let event = aEvent.
-            QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
-          let position = pivot.position;
-          let doc = aEvent.DOMNode;
-
-          let presenterContext =
-            new PresenterContext(position, event.oldAccessible);
-          let reason = event.reason;
-          this.presenters.forEach(
-            function(p) { p.pivotChanged(presenterContext, reason); });
-
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE:
-        {
-          let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
-          if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
-              !(event.isExtraState())) {
-            this.presenters.forEach(
-              function(p) {
-                p.actionInvoked(aEvent.accessible,
-                                event.isEnabled() ? 'check' : 'uncheck');
-              }
-            );
-          }
-          else if (event.state == Ci.nsIAccessibleStates.STATE_BUSY &&
-                   !(event.isExtraState()) && event.isEnabled()) {
-            let role = event.accessible.role;
-            if ((role == Ci.nsIAccessibleRole.ROLE_DOCUMENT ||
-                 role == Ci.nsIAccessibleRole.ROLE_APPLICATION)) {
-              // An existing document has changed to state "busy", this means
-              // something is loading. Send a 'loading' message to presenters.
-              this.presenters.forEach(
-                function(p) {
-                  p.tabStateChanged(event.accessible, 'loading');
-                }
-              );
-            }
-          }
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_REORDER:
-        {
-          let acc = aEvent.accessible;
-          if (acc.childCount) {
-            let docAcc = acc.getChildAt(0);
-            if (this._pendingDocuments[aEvent.DOMNode]) {
-              // This is a document in a new tab. Check if it is
-              // in a BUSY state (i.e. loading), and inform presenters.
-              // We need to do this because a state change event will not be
-              // fired when an object is created with the BUSY state.
-              // If this is not a new tab, don't bother because we sent
-              // 'loading' when the previous doc changed its state to BUSY.
-              let state = {};
-              docAcc.getState(state, {});
-              if (state.value & Ci.nsIAccessibleStates.STATE_BUSY &&
-                  this._isNotChromeDoc(docAcc))
-                this.presenters.forEach(
-                  function(p) { p.tabStateChanged(docAcc, 'loading'); }
-                );
-              delete this._pendingDocuments[aEvent.DOMNode];
-            }
-            if (this._isBrowserDoc(docAcc))
-              // A new top-level content document has been attached
-              this.presenters.forEach(
-                function(p) { p.tabStateChanged(docAcc, 'newdoc'); }
-              );
-          }
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE:
-        {
-          if (this._isNotChromeDoc(aEvent.accessible)) {
-            this.presenters.forEach(
-              function(p) {
-                p.tabStateChanged(aEvent.accessible, 'loaded');
-              }
-            );
-          }
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED:
-        {
-          this.presenters.forEach(
-            function(p) {
-              p.tabStateChanged(aEvent.accessible, 'loadstopped');
-            }
-          );
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD:
-        {
-          this.presenters.forEach(
-            function(p) {
-              p.tabStateChanged(aEvent.accessible, 'reload');
-            }
-          );
-          break;
-        }
-      case Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED:
-      case Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED:
-      {
-        if (aEvent.isFromUserInput) {
-          // XXX support live regions as well.
-          let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
-          let isInserted = event.isInserted();
-          let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
-
-          let text = '';
-          try {
-            text = txtIface.
-              getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
-          } catch (x) {
-            // XXX we might have gotten an exception with of a
-            // zero-length text. If we did, ignore it (bug #749810).
-            if (txtIface.characterCount)
-              throw x;
-          }
-
-          this.presenters.forEach(
-            function(p) {
-              p.textChanged(isInserted, event.start, event.length,
-                            text, event.modifiedText);
-            }
-          );
-        }
-        break;
-      }
-      case Ci.nsIAccessibleEvent.EVENT_SCROLLING_START:
-      {
-        VirtualCursorController.moveCursorToObject(
-          Utils.getVirtualCursor(aEvent.accessibleDocument), aEvent.accessible);
-        break;
-      }
-      case Ci.nsIAccessibleEvent.EVENT_FOCUS:
-      {
-        let acc = aEvent.accessible;
-        let doc = aEvent.accessibleDocument;
-        if (acc.role != Ci.nsIAccessibleRole.ROLE_DOCUMENT &&
-            doc.role != Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW)
-          VirtualCursorController.moveCursorToObject(
-            Utils.getVirtualCursor(doc), acc);
-
-        let [,extState] = Utils.getStates(acc);
-        let editableState = extState &
-          (Ci.nsIAccessibleStates.EXT_STATE_EDITABLE |
-           Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE);
-
-        if (editableState != VirtualCursorController.editableState) {
-          if (!VirtualCursorController.editableState)
-            this.presenters.forEach(
-              function(p) {
-                p.editingModeChanged(true);
-              }
-            );
-        }
-        VirtualCursorController.editableState = editableState;
-        break;
-      }
-      default:
-        break;
-    }
-  },
-
-  /**
-   * Check if accessible is a top-level content document (i.e. a child of a XUL
-   * browser node).
-   * @param {nsIAccessible} aDocAcc the accessible to check.
-   * @return {boolean} true if this is a top-level content document.
-   */
-  _isBrowserDoc: function _isBrowserDoc(aDocAcc) {
-    let parent = aDocAcc.parent;
-    if (!parent)
-      return false;
-
-    let domNode = parent.DOMNode;
-    if (!domNode)
-      return false;
-
-    const ns = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
-    return (domNode.localName == 'browser' && domNode.namespaceURI == ns);
-  },
-
-  /**
-   * Check if document is not a local "chrome" document, like about:home.
-   * @param {nsIDOMDocument} aDocument the document to check.
-   * @return {boolean} true if this is not a chrome document.
-   */
-  _isNotChromeDoc: function _isNotChromeDoc(aDocument) {
-    let location = aDocument.DOMNode.location;
-    if (!location)
-      return false;
-
-    return location.protocol != 'about:';
-  },
-
-  // A hash of documents that don't yet have an accessible tree.
-  _pendingDocuments: {},
-
   // So we don't enable/disable twice
   _enabled: false
 };
 
-function getAccessible(aNode) {
-  try {
-    return Cc['@mozilla.org/accessibleRetrieval;1'].
-      getService(Ci.nsIAccessibleRetrieval).getAccessibleFor(aNode);
-  } catch (e) {
-    return null;
+var Output = {
+  attach: function attach(aWindow) {
+    this.chromeWin = aWindow;
+  },
+
+  Speech: function Speech(aDetails, aBrowser) {
+    for each (let action in aDetails.actions)
+      Logger.info('tts.' + action.method, '"' + action.data + '"', JSON.stringify(action.options));
+  },
+
+  Visual: function Visual(aDetails, aBrowser) {
+    if (!this.highlightBox) {
+      // Add highlight box
+      this.highlightBox = this.chromeWin.document.
+        createElementNS('http://www.w3.org/1999/xhtml', 'div');
+      this.chromeWin.document.documentElement.appendChild(this.highlightBox);
+      this.highlightBox.id = 'virtual-cursor-box';
+
+      // Add highlight inset for inner shadow
+      let inset = this.chromeWin.document.
+        createElementNS('http://www.w3.org/1999/xhtml', 'div');
+      inset.id = 'virtual-cursor-inset';
+
+      this.highlightBox.appendChild(inset);
+    }
+
+    if (aDetails.method == 'show') {
+      let padding = aDetails.padding;
+      let r = this._adjustBounds(aDetails.bounds, aBrowser);
+
+      // First hide it to avoid flickering when changing the style.
+      this.highlightBox.style.display = 'none';
+      this.highlightBox.style.top = (r.top - padding) + 'px';
+      this.highlightBox.style.left = (r.left - padding) + 'px';
+      this.highlightBox.style.width = (r.width + padding*2) + 'px';
+      this.highlightBox.style.height = (r.height + padding*2) + 'px';
+      this.highlightBox.style.display = 'block';
+    } else if (aDetails.method == 'hide') {
+      this.highlightBox.style.display = 'none';
+    }
+  },
+
+  Android: function Android(aDetails, aBrowser) {
+    if (!this._bridge)
+      this._bridge = Cc['@mozilla.org/android/bridge;1'].getService(Ci.nsIAndroidBridge);
+
+    for each (let androidEvent in aDetails) {
+      androidEvent.type = 'Accessibility:Event';
+      if (androidEvent.bounds)
+        androidEvent.bounds = this._adjustBounds(androidEvent.bounds, aBrowser);
+      this._bridge.handleGeckoMessage(JSON.stringify({gecko: androidEvent}));
+    }
+  },
+
+  _adjustBounds: function(aJsonBounds, aBrowser) {
+    let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
+                          aJsonBounds.right - aJsonBounds.left,
+                          aJsonBounds.bottom - aJsonBounds.top);
+    let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 };
+    let browserOffset = aBrowser.getBoundingClientRect();
+
+    return bounds.translate(browserOffset.left, browserOffset.top).
+      scale(vp.zoom, vp.zoom).expandToIntegers();
   }
-}
+};
+
+var Input = {
+  editState: {},
+
+  attach: function attach(aWindow) {
+    this.chromeWin = aWindow;
+    this.chromeWin.document.addEventListener('keypress', this, true);
+    this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
+  },
+
+  detach: function detach() {
+    this.chromeWin.document.removeEventListener('keypress', this, true);
+    this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
+  },
+
+  handleEvent: function Input_handleEvent(aEvent) {
+    try {
+      switch (aEvent.type) {
+      case 'keypress':
+        this._handleKeypress(aEvent);
+        break;
+      case 'mozAccessFuGesture':
+        this._handleGesture(aEvent);
+        break;
+      }
+    } catch (x) {
+      Logger.error(x);
+    }
+  },
+
+  _handleGesture: function _handleGesture(aEvent) {
+    let detail = aEvent.detail;
+    Logger.info('Gesture', detail.type,
+                '(fingers: ' + detail.touches.length + ')');
+
+    if (detail.touches.length == 1) {
+      switch (detail.type) {
+        case 'swiperight':
+          this.moveCursor('moveNext', 'Simple', 'gestures');
+          break;
+        case 'swipeleft':
+          this.moveCursor('movePrevious', 'Simple', 'gesture');
+          break;
+        case 'doubletap':
+          this.activateCurrent();
+          break;
+        case 'explore':
+          this.moveCursor('moveToPoint', 'Simple', 'gesture',
+                          detail.x, detail.y);
+          break;
+      }
+    }
+
+    if (detail.touches.length == 3) {
+      switch (detail.type) {
+        case 'swiperight':
+          this.scroll(-1, true);
+          break;
+        case 'swipedown':
+          this.scroll(-1);
+          break;
+        case 'swipeleft':
+          this.scroll(1, true);
+          break;
+        case 'swipeup':
+          this.scroll(1);
+          break;
+      }
+    }
+  },
+
+  _handleKeypress: function _handleKeypress(aEvent) {
+    let target = aEvent.target;
+
+    // Ignore keys with modifiers so the content could take advantage of them.
+    if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
+      return;
+
+    switch (aEvent.keyCode) {
+      case 0:
+        // an alphanumeric key was pressed, handle it separately.
+        // If it was pressed with either alt or ctrl, just pass through.
+        // If it was pressed with meta, pass the key on without the meta.
+        if (this.editState.editing)
+          return;
+
+        let key = String.fromCharCode(aEvent.charCode);
+        try {
+          let [methodName, rule] = this.keyMap[key];
+          this.moveCursor(methodName, rule, 'keyboard');
+        } catch (x) {
+          return;
+        }
+        break;
+      case aEvent.DOM_VK_RIGHT:
+        if (this.editState.editing) {
+          if (!this.editState.atEnd)
+            // Don't move forward if caret is not at end of entry.
+            // XXX: Fix for rtl
+            return;
+          else
+            target.blur();
+        }
+        this.moveCursor(aEvent.shiftKey ? 'moveLast' : 'moveNext', 'Simple', 'keyboard');
+        break;
+      case aEvent.DOM_VK_LEFT:
+        if (this.editState.editing) {
+          if (!this.editState.atStart)
+            // Don't move backward if caret is not at start of entry.
+            // XXX: Fix for rtl
+            return;
+          else
+            target.blur();
+        }
+        this.moveCursor(aEvent.shiftKey ? 'moveFirst' : 'movePrevious', 'Simple', 'keyboard');
+        break;
+      case aEvent.DOM_VK_UP:
+        if (this.editState.multiline) {
+          if (!this.editState.atStart)
+            // Don't blur content if caret is not at start of text area.
+            return;
+          else
+            target.blur();
+        }
+
+        if (Utils.MozBuildApp == 'mobile/android')
+          // Return focus to native Android browser chrome.
+          Cc['@mozilla.org/android/bridge;1'].
+            getService(Ci.nsIAndroidBridge).handleGeckoMessage(
+              JSON.stringify({ gecko: { type: 'ToggleChrome:Focus' } }));
+        break;
+      case aEvent.DOM_VK_RETURN:
+      case aEvent.DOM_VK_ENTER:
+        if (this.editState.editing)
+          return;
+        this.activateCurrent();
+        break;
+    default:
+      return;
+    }
+
+    aEvent.preventDefault();
+    aEvent.stopPropagation();
+  },
+
+  moveCursor: function moveCursor(aAction, aRule, aInputType, aX, aY) {
+    let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
+    mm.sendAsyncMessage('AccessFu:VirtualCursor',
+                        {action: aAction, rule: aRule,
+                         x: aX, y: aY, origin: 'top',
+                         inputType: aInputType});
+  },
+
+  activateCurrent: function activateCurrent() {
+    let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
+    mm.sendAsyncMessage('AccessFu:Activate', {});
+  },
+
+  setEditState: function setEditState(aEditState) {
+    this.editState = aEditState;
+  },
+
+  scroll: function scroll(aPage, aHorizontal) {
+    let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
+    mm.sendAsyncMessage('AccessFu:Scroll', {page: aPage, horizontal: aHorizontal, origin: 'top'});
+  },
+
+  keyMap: {
+    a: ['moveNext', 'Anchor'],
+    A: ['movePrevious', 'Anchor'],
+    b: ['moveNext', 'Button'],
+    B: ['movePrevious', 'Button'],
+    c: ['moveNext', 'Combobox'],
+    C: ['movePrevious', 'Combobox'],
+    e: ['moveNext', 'Entry'],
+    E: ['movePrevious', 'Entry'],
+    f: ['moveNext', 'FormElement'],
+    F: ['movePrevious', 'FormElement'],
+    g: ['moveNext', 'Graphic'],
+    G: ['movePrevious', 'Graphic'],
+    h: ['moveNext', 'Heading'],
+    H: ['movePrevious', 'Heading'],
+    i: ['moveNext', 'ListItem'],
+    I: ['movePrevious', 'ListItem'],
+    k: ['moveNext', 'Link'],
+    K: ['movePrevious', 'Link'],
+    l: ['moveNext', 'List'],
+    L: ['movePrevious', 'List'],
+    p: ['moveNext', 'PageTab'],
+    P: ['movePrevious', 'PageTab'],
+    r: ['moveNext', 'RadioButton'],
+    R: ['movePrevious', 'RadioButton'],
+    s: ['moveNext', 'Separator'],
+    S: ['movePrevious', 'Separator'],
+    t: ['moveNext', 'Table'],
+    T: ['movePrevious', 'Table'],
+    x: ['moveNext', 'Checkbox'],
+    X: ['movePrevious', 'Checkbox']
+  }
+};
new file mode 100644
--- /dev/null
+++ b/accessible/src/jsat/EventManager.jsm
@@ -0,0 +1,299 @@
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import('resource://gre/modules/accessibility/Presenters.jsm');
+Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+var EXPORTED_SYMBOLS = ['EventManager'];
+
+var EventManager = {
+  editState: {},
+
+  start: function start(aSendMsgFunc) {
+    try {
+      if (!this._started) {
+        this.sendMsgFunc = aSendMsgFunc || function() {};
+        this.presenters = [new VisualPresenter()];
+
+        if (Utils.MozBuildApp == 'b2g') {
+          this.presenters.push(new SpeechPresenter());
+        } else if (Utils.MozBuildApp == 'mobile/android') {
+          this.presenters.push(new AndroidPresenter());
+        }
+
+        Logger.info('EventManager.start', Utils.MozBuildApp, [p.type for each(p in this.presenters)].join(', '));
+
+        this._started = true;
+        Services.obs.addObserver(this, 'accessible-event', false);
+      }
+
+      this.present(
+        function(p) {
+          return p.tabStateChanged(null, 'newtab');
+        }
+      );
+    } catch (x) {
+      Logger.error('Failed to start EventManager:', x);
+    }
+  },
+
+  stop: function stop() {
+    Services.obs.removeObserver(this, 'accessible-event');
+    this.presenters = [];
+    this._started = false;
+  },
+
+  handleEvent: function handleEvent(aEvent) {
+    try {
+      switch (aEvent.type) {
+      case 'DOMActivate':
+      {
+        let activatedAcc =
+          Utils.AccRetrieval.getAccessibleFor(aEvent.originalTarget);
+        let [state, extState] = Utils.getStates(activatedAcc);
+
+        // Checkable objects will have a state changed event that we will use
+        // instead of this hackish DOMActivate. We will also know the true
+        // action that was taken.
+        if (state & Ci.nsIAccessibleStates.STATE_CHECKABLE)
+          return;
+
+        this.present(
+          function(p) {
+            return p.actionInvoked(activatedAcc, 'click');
+          }
+        );
+        break;
+      }
+      case 'scroll':
+      case 'resize':
+      {
+        this.present(
+          function(p) {
+            return p.viewportChanged();;
+          }
+        );
+        break;
+      }
+      }
+    } catch (x) {
+      Logger.error('Error handling DOM event:', x);
+    }
+  },
+
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case 'accessible-event':
+        var event;
+        try {
+          event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
+          this.handleAccEvent(event);
+        } catch (x) {
+          Logger.error('Error handing accessible event:', x);
+          return;
+        }
+    }
+  },
+
+  presentLastPivot: function presentLastPivot() {
+    this.present(
+      function(p) {
+        return p.presentLastPivot();
+      }
+    );
+  },
+
+  handleAccEvent: function handleAccEvent(aEvent) {
+    if (Logger.logLevel >= Logger.DEBUG)
+      Logger.debug('A11yEvent', Logger.eventToString(aEvent),
+                   Logger.accessibleToString(aEvent.accessible));
+
+    switch (aEvent.eventType) {
+      case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
+      {
+        let pivot = aEvent.accessible.
+          QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
+        let position = pivot.position;
+        if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME)
+          break;
+        let event = aEvent.
+          QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
+        let presenterContext =
+          new PresenterContext(position, event.oldAccessible);
+        let reason = event.reason;
+
+        if (this.editState.editing)
+          aEvent.accessibleDocument.takeFocus();
+
+        this.present(
+          function(p) {
+            return p.pivotChanged(presenterContext, reason);
+          }
+        );
+        break;
+      }
+      case Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE:
+      {
+        let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
+        if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
+            !(event.isExtraState())) {
+          this.present(
+            function(p) {
+              return p.actionInvoked(aEvent.accessible,
+                                     event.isEnabled() ? 'check' : 'uncheck');
+            }
+          );
+        }
+        break;
+      }
+      case Ci.nsIAccessibleEvent.EVENT_SCROLLING_START:
+      {
+        let vc = Utils.getVirtualCursor(aEvent.accessibleDocument);
+        vc.moveNext(TraversalRules.Simple, aEvent.accessible, true);
+        break;
+      }
+      case Ci.nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED:
+      {
+        let acc = aEvent.accessible;
+        let characterCount = acc.
+          QueryInterface(Ci.nsIAccessibleText).characterCount;
+        let caretOffset = aEvent.
+          QueryInterface(Ci.nsIAccessibleCaretMoveEvent).caretOffset;
+
+        // Update editing state, both for presenter and other things
+        let [,extState] = Utils.getStates(acc);
+        let editState = {
+          editing: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_EDITABLE),
+          multiline: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE),
+          atStart: caretOffset == 0,
+          atEnd: caretOffset == characterCount
+        };
+
+        // Not interesting
+        if (!editState.editing && editState.editing == this.editState.editing)
+          break;
+
+        if (editState.editing != this.editState.editing)
+          this.present(
+            function(p) {
+              return p.editingModeChanged(editState.editing);
+            }
+          );
+
+        if (editState.editing != this.editState.editing ||
+            editState.multiline != this.editState.multiline ||
+            editState.atEnd != this.editState.atEnd ||
+            editState.atStart != this.editState.atStart)
+          this.sendMsgFunc("AccessFu:Input", editState);
+
+        this.editState = editState;
+        break;
+      }
+      case Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED:
+      case Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED:
+      {
+        if (aEvent.isFromUserInput) {
+          // XXX support live regions as well.
+          let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
+          let isInserted = event.isInserted();
+          let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
+
+          let text = '';
+          try {
+            text = txtIface.
+              getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
+          } catch (x) {
+            // XXX we might have gotten an exception with of a
+            // zero-length text. If we did, ignore it (bug #749810).
+            if (txtIface.characterCount)
+              throw x;
+          }
+          this.present(
+            function(p) {
+              return p.textChanged(isInserted, event.start, event.length,
+                                   text, event.modifiedText);
+            }
+          );
+        }
+        break;
+      }
+      case Ci.nsIAccessibleEvent.EVENT_FOCUS:
+      {
+        // Put vc where the focus is at
+        let acc = aEvent.accessible;
+        let doc = aEvent.accessibleDocument;
+        if (acc.role != Ci.nsIAccessibleRole.ROLE_DOCUMENT &&
+            doc.role != Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW) {
+          let vc = Utils.getVirtualCursor(doc);
+          vc.moveNext(TraversalRules.Simple, acc, true);
+        }
+        break;
+      }
+    }
+  },
+
+  present: function present(aPresenterFunc) {
+    try {
+      this.sendMsgFunc(
+        "AccessFu:Present",
+        [aPresenterFunc(p) for each (p in this.presenters)].
+          filter(function(d) {return !!d;}));
+    } catch (x) {
+      Logger.error(x);
+    }
+  },
+
+  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+    let tabstate = '';
+
+    let loadingState = Ci.nsIWebProgressListener.STATE_TRANSFERRING |
+      Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
+    let loadedState = Ci.nsIWebProgressListener.STATE_STOP |
+      Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+
+    if ((aStateFlags & loadingState) == loadingState) {
+      tabstate = 'loading';
+    } else if ((aStateFlags & loadedState) == loadedState &&
+               !aWebProgress.isLoadingDocument) {
+      tabstate = 'loaded';
+    }
+
+    if (tabstate) {
+      let docAcc = Utils.AccRetrieval.getAccessibleFor(aWebProgress.DOMWindow.document);
+      this.present(
+        function(p) {
+          return p.tabStateChanged(docAcc, tabstate);
+        }
+      );
+    }
+  },
+
+  onProgressChange: function onProgressChange() {},
+
+  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+    let docAcc = Utils.AccRetrieval.getAccessibleFor(aWebProgress.DOMWindow.document);
+    this.present(
+      function(p) {
+        return p.tabStateChanged(docAcc, 'newdoc');
+      }
+    );
+  },
+
+  onStatusChange: function onStatusChange() {},
+
+  onSecurityChange: function onSecurityChange() {},
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsISupports,
+                                         Ci.nsIObserver])
+};
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -22,16 +22,21 @@ var EXPORTED_SYMBOLS = ['VisualPresenter
 /**
  * The interface for all presenter classes. A presenter could be, for example,
  * a speech output module, or a visual cursor indicator.
  */
 function Presenter() {}
 
 Presenter.prototype = {
   /**
+   * The type of presenter. Used for matching it with the appropriate output method.
+   */
+  type: 'Base',
+
+  /**
    * Attach function for presenter.
    * @param {ChromeWindow} aWindow Chrome window the presenter could use.
    */
   attach: function attach(aWindow) {},
 
   /**
    * Detach function.
    */
@@ -87,318 +92,141 @@ Presenter.prototype = {
    * @param {PresenterContext} aVCContext context object for tab's current
    *   virtual cursor position.
    */
   tabSelected: function tabSelected(aDocContext, aVCContext) {},
 
   /**
    * The viewport has changed, either a scroll, pan, zoom, or
    *    landscape/portrait toggle.
+   * @param {Window} aWindow window of viewport that changed.
    */
-  viewportChanged: function viewportChanged() {},
+  viewportChanged: function viewportChanged(aWindow) {},
 
   /**
    * We have entered or left text editing mode.
    */
-  editingModeChanged: function editingModeChanged(aIsEditing) {}
+  editingModeChanged: function editingModeChanged(aIsEditing) {},
+
+  /**
+   * Re-present the last pivot change.
+   */
+  presentLastPivot: function AndroidPresenter_presentLastPivot() {}
 };
 
 /**
  * Visual presenter. Draws a box around the virtual cursor's position.
  */
 
 function VisualPresenter() {}
 
 VisualPresenter.prototype = {
   __proto__: Presenter.prototype,
 
+  type: 'Visual',
+
   /**
    * The padding in pixels between the object and the highlight border.
    */
   BORDER_PADDING: 2,
 
-  attach: function VisualPresenter_attach(aWindow) {
-    this.chromeWin = aWindow;
-
-    // Add highlight box
-    this.highlightBox = this.chromeWin.document.
-      createElementNS('http://www.w3.org/1999/xhtml', 'div');
-    this.chromeWin.document.documentElement.appendChild(this.highlightBox);
-    this.highlightBox.id = 'virtual-cursor-box';
+  viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
+    if (this._currentContext)
+      return {
+        type: this.type,
+        details: {
+          method: 'show',
+          bounds: this._currentContext.bounds,
+          padding: this.BORDER_PADDING
+        }
+      };
 
-    // Add highlight inset for inner shadow
-    let inset = this.chromeWin.document.
-      createElementNS('http://www.w3.org/1999/xhtml', 'div');
-    inset.id = 'virtual-cursor-inset';
-
-    this.highlightBox.appendChild(inset);
-  },
-
-  detach: function VisualPresenter_detach() {
-    this.highlightBox.parentNode.removeChild(this.highlightBox);
-    this.highlightBox = this.stylesheet = null;
-  },
-
-  viewportChanged: function VisualPresenter_viewportChanged() {
-    if (this._currentContext)
-      this._highlight(this._currentContext);
+    return null;
   },
 
   pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
     this._currentContext = aContext;
 
-    if (!aContext.accessible) {
-      this._hide();
-      return;
-    }
+    if (!aContext.accessible)
+      return {type: this.type, details: {method: 'hide'}};
 
     try {
       aContext.accessible.scrollTo(
         Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
-      this._highlight(aContext);
+      return {
+        type: this.type,
+        details: {
+          method: 'show',
+          bounds: aContext.bounds,
+          padding: this.BORDER_PADDING
+        }
+      };
     } catch (e) {
       Logger.error('Failed to get bounds: ' + e);
-      return;
+      return null;
     }
   },
 
   tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
-    this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
+    return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
   },
 
   tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
                                                             aPageState) {
     if (aPageState == 'newdoc')
-      this._hide();
-  },
-
-  // Internals
-
-  _hide: function _hide() {
-    this.highlightBox.style.display = 'none';
-  },
+      return {type: this.type, details: {method: 'hide'}};
 
-  _highlight: function _highlight(aContext) {
-    let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 };
-    let r = aContext.bounds.scale(vp.zoom, vp.zoom).expandToIntegers();
-
-    // First hide it to avoid flickering when changing the style.
-    this.highlightBox.style.display = 'none';
-    this.highlightBox.style.top = (r.top - this.BORDER_PADDING) + 'px';
-    this.highlightBox.style.left = (r.left - this.BORDER_PADDING) + 'px';
-    this.highlightBox.style.width = (r.width + this.BORDER_PADDING*2) + 'px';
-    this.highlightBox.style.height = (r.height + this.BORDER_PADDING*2) + 'px';
-    this.highlightBox.style.display = 'block';
+    return null;
   }
 };
 
 /**
  * Android presenter. Fires Android a11y events.
  */
 
 function AndroidPresenter() {}
 
 AndroidPresenter.prototype = {
   __proto__: Presenter.prototype,
 
+  type: 'Android',
+
   // Android AccessibilityEvent type constants.
   ANDROID_VIEW_CLICKED: 0x01,
   ANDROID_VIEW_LONG_CLICKED: 0x02,
   ANDROID_VIEW_SELECTED: 0x04,
   ANDROID_VIEW_FOCUSED: 0x08,
   ANDROID_VIEW_TEXT_CHANGED: 0x10,
   ANDROID_WINDOW_STATE_CHANGED: 0x20,
   ANDROID_VIEW_HOVER_ENTER: 0x80,
   ANDROID_VIEW_HOVER_EXIT: 0x100,
   ANDROID_VIEW_SCROLLED: 0x1000,
   ANDROID_ANNOUNCEMENT: 0x4000,
   ANDROID_VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
 
-  attach: function AndroidPresenter_attach(aWindow) {
-    this.chromeWin = aWindow;
-  },
-
   pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
     if (!aContext.accessible)
-      return;
+      return null;
 
     this._currentContext = aContext;
 
+    let androidEvents = [];
+
     let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
                             Utils.AndroidSdkVersion >= 14);
     let focusEventType = (Utils.AndroidSdkVersion >= 16) ?
       this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED :
       this.ANDROID_VIEW_FOCUSED;
 
     if (isExploreByTouch) {
       // This isn't really used by TalkBack so this is a half-hearted attempt
       // for now.
-      this.sendMessageToJava({
-         gecko: {
-           type: 'Accessibility:Event',
-           eventType: this.ANDROID_VIEW_HOVER_EXIT,
-           text: []
-         }
-      });
-    }
-
-    let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 };
-    let bounds = aContext.bounds.scale(vp.zoom, vp.zoom).expandToIntegers();
-    let output = [];
-
-    aContext.newAncestry.forEach(
-      function(acc) {
-        output.push.apply(output, UtteranceGenerator.genForObject(acc));
-      }
-    );
-
-    output.push.apply(output,
-                      UtteranceGenerator.genForObject(aContext.accessible));
-
-    aContext.subtreePreorder.forEach(
-      function(acc) {
-        output.push.apply(output, UtteranceGenerator.genForObject(acc));
-      }
-    );
-
-    this.sendMessageToJava({
-      gecko: {
-        type: 'Accessibility:Event',
-        eventType: (isExploreByTouch) ? this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
-        text: output,
-        bounds: bounds
-      }
-    });
-  },
-
-  actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
-    this.sendMessageToJava({
-      gecko: {
-        type: 'Accessibility:Event',
-        eventType: this.ANDROID_VIEW_CLICKED,
-        text: UtteranceGenerator.genForAction(aObject, aActionName)
-      }
-    });
-  },
-
-  tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
-    // Send a pivot change message with the full context utterance for this doc.
-    this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
-  },
-
-  tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
-                                                             aPageState) {
-    this._appAnnounce(
-      UtteranceGenerator.genForTabStateChange(aDocObj, aPageState));
-  },
-
-  textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
-                                                     aLength, aText,
-                                                     aModifiedText) {
-    let androidEvent = {
-      type: 'Accessibility:Event',
-      eventType: this.ANDROID_VIEW_TEXT_CHANGED,
-      text: [aText],
-      fromIndex: aStart,
-      removedCount: 0,
-      addedCount: 0
-    };
-
-    if (aIsInserted) {
-      androidEvent.addedCount = aLength;
-      androidEvent.beforeText =
-        aText.substring(0, aStart) + aText.substring(aStart + aLength);
-    } else {
-      androidEvent.removedCount = aLength;
-      androidEvent.beforeText =
-        aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
+      androidEvents.push({eventType: this.ANDROID_VIEW_HOVER_EXIT, text: []});
     }
 
-    this.sendMessageToJava({gecko: androidEvent});
-  },
-
-  viewportChanged: function AndroidPresenter_viewportChanged() {
-    if (Utils.AndroidSdkVersion < 14)
-      return;
-
-    let win = Utils.getBrowserApp(this.chromeWin).selectedBrowser.contentWindow;
-    this.sendMessageToJava({
-      gecko: {
-        type: 'Accessibility:Event',
-        eventType: this.ANDROID_VIEW_SCROLLED,
-        text: [],
-        scrollX: win.scrollX,
-        scrollY: win.scrollY,
-        maxScrollX: win.scrollMaxX,
-        maxScrollY: win.scrollMaxY
-      }
-    });
-  },
-
-  editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
-    this._appAnnounce(UtteranceGenerator.genForEditingMode(aIsEditing));
-  },
-
-  _appAnnounce: function _appAnnounce(aUtterance) {
-    if (!aUtterance.length)
-      return;
-
-    this.sendMessageToJava({
-      gecko: {
-        type: 'Accessibility:Event',
-        eventType: (Utils.AndroidSdkVersion >= 16) ?
-          this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
-        text: aUtterance,
-        addedCount: aUtterance.join(' ').length,
-        removedCount: 0,
-        fromIndex: 0
-      }
-    });
-  },
-
-  accessibilityFocus: function AndroidPresenter_accessibilityFocus() {
-    if (this._currentContext)
-      this.pivotChanged(this._currentContext);
-  },
-
-  sendMessageToJava: function AndroidPresenter_sendMessageTojava(aMessage) {
-    return Cc['@mozilla.org/android/bridge;1'].
-      getService(Ci.nsIAndroidBridge).
-      handleGeckoMessage(JSON.stringify(aMessage));
-  }
-};
-
-/**
- * A dummy Android presenter for desktop testing
- */
-
-function DummyAndroidPresenter() {}
-
-DummyAndroidPresenter.prototype = {
-  __proto__: AndroidPresenter.prototype,
-
-  sendMessageToJava: function DummyAndroidPresenter_sendMessageToJava(aMsg) {
-    Logger.debug('Android event:\n' + JSON.stringify(aMsg, null, 2));
-  }
-};
-
-/**
- * A speech presenter for direct TTS output
- */
-
-function SpeechPresenter() {}
-
-SpeechPresenter.prototype = {
-  __proto__: Presenter.prototype,
-
-
-  pivotChanged: function SpeechPresenter_pivotChanged(aContext, aReason) {
-    if (!aContext.accessible)
-      return;
-
     let output = [];
 
     aContext.newAncestry.forEach(
       function(acc) {
         output.push.apply(output, UtteranceGenerator.genForObject(acc));
       }
     );
 
@@ -406,19 +234,163 @@ SpeechPresenter.prototype = {
                       UtteranceGenerator.genForObject(aContext.accessible));
 
     aContext.subtreePreorder.forEach(
       function(acc) {
         output.push.apply(output, UtteranceGenerator.genForObject(acc));
       }
     );
 
-    Logger.info('SPEAK', '"' + output.join(' ') + '"');
+    androidEvents.push({eventType: (isExploreByTouch) ?
+                          this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
+                        text: output,
+                        bounds: aContext.bounds});
+    return {
+      type: this.type,
+      details: androidEvents
+    };
+  },
+
+  actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
+    return {
+      type: this.type,
+      details: [{
+        eventType: this.ANDROID_VIEW_CLICKED,
+        text: UtteranceGenerator.genForAction(aObject, aActionName)
+      }]
+    };
+  },
+
+  tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
+    // Send a pivot change message with the full context utterance for this doc.
+    return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
+  },
+
+  tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
+                                                             aPageState) {
+    return this._appAnnounce(
+      UtteranceGenerator.genForTabStateChange(aDocObj, aPageState));
+  },
+
+  textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
+                                                     aLength, aText,
+                                                     aModifiedText) {
+    let androidEvent = {
+      type: this.type,
+      details: [{
+        eventType: this.ANDROID_VIEW_TEXT_CHANGED,
+        text: [aText],
+        fromIndex: aStart,
+        removedCount: 0,
+        addedCount: 0
+      }]
+    };
+
+    if (aIsInserted) {
+      androidEvent.addedCount = aLength;
+      androidEvent.beforeText =
+        aText.substring(0, aStart) + aText.substring(aStart + aLength);
+    } else {
+      androidEvent.removedCount = aLength;
+      androidEvent.beforeText =
+        aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
+    }
+
+    return androidEvent;
+  },
+
+  viewportChanged: function AndroidPresenter_viewportChanged(aWindow) {
+    if (Utils.AndroidSdkVersion < 14)
+      return null;
+
+    return {
+      type: this.type,
+      details: [{
+        eventType: this.ANDROID_VIEW_SCROLLED,
+        text: [],
+        scrollX: aWindow.scrollX,
+        scrollY: aWindow.scrollY,
+        maxScrollX: aWindow.scrollMaxX,
+        maxScrollY: aWindow.scrollMaxY
+      }]
+    };
+  },
+
+  editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
+    return this._appAnnounce(UtteranceGenerator.genForEditingMode(aIsEditing));
+  },
+
+  _appAnnounce: function _appAnnounce(aUtterance) {
+    if (!aUtterance.length)
+      return null;
+
+    return {
+      type: this.type,
+      details: [{
+        eventType: (Utils.AndroidSdkVersion >= 16) ?
+          this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
+        text: aUtterance,
+        addedCount: aUtterance.join(' ').length,
+        removedCount: 0,
+        fromIndex: 0
+      }]
+    };
+  },
+
+  presentLastPivot: function AndroidPresenter_presentLastPivot() {
+    if (this._currentContext)
+      return this.pivotChanged(this._currentContext);
+    else
+      return null;
   }
-}
+};
+
+/**
+ * A speech presenter for direct TTS output
+ */
+
+function SpeechPresenter() {}
+
+SpeechPresenter.prototype = {
+  __proto__: Presenter.prototype,
+
+  type: 'Speech',
+
+  pivotChanged: function SpeechPresenter_pivotChanged(aContext, aReason) {
+    if (!aContext.accessible)
+      return null;
+
+    let output = [];
+
+    aContext.newAncestry.forEach(
+      function(acc) {
+        output.push.apply(output, UtteranceGenerator.genForObject(acc));
+      }
+    );
+
+    output.push.apply(output,
+                      UtteranceGenerator.genForObject(aContext.accessible));
+
+    aContext.subtreePreorder.forEach(
+      function(acc) {
+        output.push.apply(output, UtteranceGenerator.genForObject(acc));
+      }
+    );
+
+    return {
+      type: this.type,
+      details: {
+        actions: [
+          {method: 'playEarcon', data: 'tick', options: {}},
+          {method: 'speak', data: output.join(' '), options: {enqueue: true}}
+        ]
+      }
+    };
+  }
+};
 
 /**
  * PresenterContext: An object that generates and caches context information
  * for a given accessible and its relationship with another accessible.
  */
 function PresenterContext(aAccessible, aOldAccessible) {
   this._accessible = aAccessible;
   this._oldAccessible =
@@ -499,17 +471,18 @@ PresenterContext.prototype = {
   },
 
   get bounds() {
     if (!this._bounds) {
       let objX = {}, objY = {}, objW = {}, objH = {};
 
       this._accessible.getBounds(objX, objY, objW, objH);
 
-      // Can't specify relative coords in nsIAccessible.getBounds, so we do it.
+      // XXX: OOP content provides a screen offset of 0, while in-process provides a real
+      // offset. Removing the offset and using content-relative coords normalizes this.
       let docX = {}, docY = {};
       let docRoot = this._accessible.rootDocument.
         QueryInterface(Ci.nsIAccessible);
       docRoot.getBounds(docX, docY, {}, {});
 
       this._bounds = new Rect(objX.value, objY.value, objW.value, objH.value).
         translate(-docX.value, -docY.value);
     }
new file mode 100644
--- /dev/null
+++ b/accessible/src/jsat/TraversalRules.jsm
@@ -0,0 +1,207 @@
+/* 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';
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+var EXPORTED_SYMBOLS = ['TraversalRules'];
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+function BaseTraversalRule(aRoles, aMatchFunc) {
+  this._matchRoles = aRoles;
+  this._matchFunc = aMatchFunc;
+}
+
+BaseTraversalRule.prototype = {
+    getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
+      aRules.value = this._matchRoles;
+      return aRules.value.length;
+    },
+
+    preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
+    Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
+
+    match: function BaseTraversalRule_match(aAccessible)
+    {
+      if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
+        return (aAccessible.childCount) ?
+          Ci.nsIAccessibleTraversalRule.FILTER_IGNORE :
+          Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+      }
+
+      if (this._matchFunc)
+        return this._matchFunc(aAccessible);
+
+      return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+    },
+
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
+};
+
+var gSimpleTraversalRoles =
+  [Ci.nsIAccessibleRole.ROLE_MENUITEM,
+   Ci.nsIAccessibleRole.ROLE_LINK,
+   Ci.nsIAccessibleRole.ROLE_PAGETAB,
+   Ci.nsIAccessibleRole.ROLE_GRAPHIC,
+   // XXX: Find a better solution for ROLE_STATICTEXT.
+   // It allows to filter list bullets but at the same time it
+   // filters CSS generated content too as an unwanted side effect.
+   // Ci.nsIAccessibleRole.ROLE_STATICTEXT,
+   Ci.nsIAccessibleRole.ROLE_TEXT_LEAF,
+   Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
+   Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
+   Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
+   Ci.nsIAccessibleRole.ROLE_COMBOBOX,
+   Ci.nsIAccessibleRole.ROLE_PROGRESSBAR,
+   Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
+   Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
+   Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
+   Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
+   Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
+   Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
+   Ci.nsIAccessibleRole.ROLE_ENTRY,
+   // Used for traversing in to child OOP frames.
+   Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME];
+
+var TraversalRules = {
+  Simple: new BaseTraversalRule(
+    gSimpleTraversalRoles,
+    function Simple_match(aAccessible) {
+      switch (aAccessible.role) {
+      case Ci.nsIAccessibleRole.ROLE_COMBOBOX:
+        // We don't want to ignore the subtree because this is often
+        // where the list box hangs out.
+        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+      case Ci.nsIAccessibleRole.ROLE_TEXT_LEAF:
+        {
+          // Nameless text leaves are boring, skip them.
+          let name = aAccessible.name;
+          if (name && name.trim())
+            return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+          else
+            return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
+        }
+      case Ci.nsIAccessibleRole.ROLE_LINK:
+        // If the link has children we should land on them instead.
+        // Image map links don't have children so we need to match those.
+        if (aAccessible.childCount == 0)
+          return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+        else
+          return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
+      default:
+        // Ignore the subtree, if there is one. So that we don't land on
+        // the same content that was already presented by its parent.
+        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
+          Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
+      }
+    }
+  ),
+
+  SimpleTouch: new BaseTraversalRule(
+    gSimpleTraversalRoles,
+    function Simple_match(aAccessible) {
+      return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
+        Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
+    }
+  ),
+
+  Anchor: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_LINK],
+    function Anchor_match(aAccessible)
+    {
+      // We want to ignore links, only focus named anchors.
+      let state = {};
+      let extraState = {};
+      aAccessible.getState(state, extraState);
+      if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
+        return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
+      } else {
+        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+      }
+    }),
+
+  Button: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
+     Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
+     Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
+     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
+     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID]),
+
+  Combobox: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_COMBOBOX,
+     Ci.nsIAccessibleRole.ROLE_LISTBOX]),
+
+  Entry: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_ENTRY,
+     Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT]),
+
+  FormElement: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
+     Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
+     Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
+     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
+     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID,
+     Ci.nsIAccessibleRole.ROLE_COMBOBOX,
+     Ci.nsIAccessibleRole.ROLE_LISTBOX,
+     Ci.nsIAccessibleRole.ROLE_ENTRY,
+     Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
+     Ci.nsIAccessibleRole.ROLE_PAGETAB,
+     Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
+     Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
+     Ci.nsIAccessibleRole.ROLE_SLIDER,
+     Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
+     Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM]),
+
+  Graphic: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_GRAPHIC]),
+
+  Heading: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_HEADING]),
+
+  ListItem: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_LISTITEM,
+     Ci.nsIAccessibleRole.ROLE_TERM]),
+
+  Link: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_LINK],
+    function Link_match(aAccessible)
+    {
+      // We want to ignore anchors, only focus real links.
+      let state = {};
+      let extraState = {};
+      aAccessible.getState(state, extraState);
+      if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
+        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
+      } else {
+        return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
+      }
+    }),
+
+  List: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_LIST,
+     Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST]),
+
+  PageTab: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_PAGETAB]),
+
+  RadioButton: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
+     Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM]),
+
+  Separator: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_SEPARATOR]),
+
+  Table: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_TABLE]),
+
+  Checkbox: new BaseTraversalRule(
+    [Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
+     Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM])
+};
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -24,28 +24,39 @@ var Utils = {
     if (!this._AccRetrieval) {
       this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
         getService(Ci.nsIAccessibleRetrieval);
     }
 
     return this._AccRetrieval;
   },
 
+  set MozBuildApp(value) {
+    this._buildApp = value;
+  },
+
   get MozBuildApp() {
     if (!this._buildApp)
       this._buildApp = this._buildAppMap[Services.appinfo.ID];
     return this._buildApp;
   },
 
   get OS() {
     if (!this._OS)
       this._OS = Services.appinfo.OS;
     return this._OS;
   },
 
+  get ScriptName() {
+    if (!this._ScriptName)
+      this._ScriptName =
+        (Services.appinfo.processType == 2) ? 'AccessFuContent' : 'AccessFu';
+    return this._ScriptName;
+  },
+
   get AndroidSdkVersion() {
     if (!this._AndroidSdkVersion) {
       let shellVersion = Services.sysinfo.get('shellVersion') || '';
       let matches = shellVersion.match(/\((\d+)\)$/);
       if (matches)
         this._AndroidSdkVersion = parseInt(matches[1]);
       else
         this._AndroidSdkVersion = 15; // Most useful in desktop debugging.
@@ -66,34 +77,51 @@ var Utils = {
         return aWindow.gBrowser;
       case 'b2g':
         return aWindow.shell;
       default:
         return null;
     }
   },
 
+  getCurrentBrowser: function getCurrentBrowser(aWindow) {
+    if (this.MozBuildApp == 'b2g')
+      return this.getBrowserApp(aWindow).contentBrowser;
+    return this.getBrowserApp(aWindow).selectedBrowser;
+  },
+
   getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
-    if (this.MozBuildApp == "b2g")
-      return this.getBrowserApp(aWindow).contentBrowser.contentDocument;
-    return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
+    return this.getCurrentBrowser(aWindow).contentDocument;
   },
 
-  getAllDocuments: function getAllDocuments(aWindow) {
-    let doc = this.AccRetrieval.
-      getAccessibleFor(this.getCurrentContentDoc(aWindow)).
-      QueryInterface(Ci.nsIAccessibleDocument);
-    let docs = [];
-    function getAllDocuments(aDocument) {
-      docs.push(aDocument.DOMDocument);
-      for (let i = 0; i < aDocument.childDocumentCount; i++)
-        getAllDocuments(aDocument.getChildDocumentAt(i));
+  getMessageManager: function getMessageManager(aBrowser) {
+    try {
+      return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
+         frameLoader.messageManager;
+    } catch (x) {
+      Logger.error(x);
+      return null;
     }
-    getAllDocuments(doc);
-    return docs;
+  },
+
+  getAllMessageManagers: function getAllMessageManagers(aWindow) {
+    let messageManagers = [];
+
+    for (let i = 0; i < aWindow.messageManager.childCount; i++)
+      messageManagers.push(aWindow.messageManager.getChildAt(i));
+
+    let remoteframes = this.getCurrentContentDoc(aWindow).
+      querySelectorAll('iframe[remote=true]');
+
+    for (let i = 0; i < remoteframes.length; ++i)
+      messageManagers.push(this.getMessageManager(remoteframes[i]));
+
+    Logger.info(messageManagers.length);
+
+    return messageManagers;
   },
 
   getViewport: function getViewport(aWindow) {
     switch (this.MozBuildApp) {
       case 'mobile/android':
         return aWindow.BrowserApp.selectedTab.getViewport();
       default:
         return null;
@@ -118,93 +146,16 @@ var Utils = {
       try {
         return doc.QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
       } catch (x) {
         doc = doc.parentDocument;
       }
     }
 
     return null;
-  },
-
-  scroll: function scroll(aWindow, aPage, aHorizontal) {
-    for each (let doc in this.getAllDocuments(aWindow)) {
-      // First see if we could scroll a window.
-      let win = doc.defaultView;
-      if (!aHorizontal && win.scrollMaxY &&
-          ((aPage > 0 && win.scrollY < win.scrollMaxY) ||
-           (aPage < 0 && win.scrollY > 0))) {
-        win.scroll(0, win.innerHeight);
-        return true;
-      } else if (aHorizontal && win.scrollMaxX &&
-                 ((aPage > 0 && win.scrollX < win.scrollMaxX) ||
-                  (aPage < 0 && win.scrollX > 0))) {
-        win.scroll(win.innerWidth, 0);
-        return true;
-      }
-
-      // Second, try to scroll main section or current target if there is no
-      // main section.
-      let main = doc.querySelector('[role=main]') ||
-        doc.querySelector(':target');
-
-      if (main) {
-        if ((!aHorizontal && main.clientHeight < main.scrollHeight) ||
-          (aHorizontal && main.clientWidth < main.scrollWidth)) {
-          let s = win.getComputedStyle(main);
-          if (!aHorizontal) {
-            if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
-              main.scrollTop += aPage * main.clientHeight;
-              return true;
-            }
-          } else {
-            if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
-              main.scrollLeft += aPage * main.clientWidth;
-              return true;
-            }
-          }
-        }
-      }
-    }
-
-    return false;
-  },
-
-  changePage: function changePage(aWindow, aPage) {
-    for each (let doc in this.getAllDocuments(aWindow)) {
-      // Get current main section or active target.
-      let main = doc.querySelector('[role=main]') ||
-        doc.querySelector(':target');
-      if (!main)
-        continue;
-
-      let mainAcc = this.AccRetrieval.getAccessibleFor(main);
-      if (!mainAcc)
-        continue;
-
-      let controllers = mainAcc.
-        getRelationByType(Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
-
-      for (var i=0; controllers.targetsCount > i; i++) {
-        let controller = controllers.getTarget(i);
-        // If the section has a controlling slider, it should be considered
-        // the page-turner.
-        if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
-          // Sliders are controlled with ctrl+right/left. I just decided :)
-          let evt = doc.createEvent("KeyboardEvent");
-          evt.initKeyEvent('keypress', true, true, null,
-                           true, false, false, false,
-                           (aPage > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
-          controller.DOMNode.dispatchEvent(evt);
-          return true;
-        }
-      }
-    }
-
-    return false;
   }
 };
 
 var Logger = {
   DEBUG: 0,
   INFO: 1,
   WARNING: 2,
   ERROR: 3,
@@ -212,17 +163,18 @@ var Logger = {
 
   logLevel: 1, // INFO;
 
   log: function log(aLogLevel) {
     if (aLogLevel < this.logLevel)
       return;
 
     let message = Array.prototype.slice.call(arguments, 1).join(' ');
-    dump('[AccessFu] ' + this._LEVEL_NAMES[aLogLevel] + ' ' + message + '\n');
+    dump('[' + Utils.ScriptName + '] ' +
+         this._LEVEL_NAMES[aLogLevel] +' ' + message + '\n');
   },
 
   info: function info() {
     this.log.apply(
       this, [this.INFO].concat(Array.prototype.slice.call(arguments)));
   },
 
   debug: function debug() {
deleted file mode 100644
--- a/accessible/src/jsat/VirtualCursorController.jsm
+++ /dev/null
@@ -1,434 +0,0 @@
-/* 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';
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-var EXPORTED_SYMBOLS = ['VirtualCursorController'];
-
-Cu.import('resource://gre/modules/accessibility/Utils.jsm');
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-
-function BaseTraversalRule(aRoles, aMatchFunc) {
-  this._matchRoles = aRoles;
-  this._matchFunc = aMatchFunc;
-}
-
-BaseTraversalRule.prototype = {
-    getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
-      aRules.value = this._matchRoles;
-      return aRules.value.length;
-    },
-
-    preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
-    Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
-
-    match: function BaseTraversalRule_match(aAccessible)
-    {
-      if (this._matchFunc)
-        return this._matchFunc(aAccessible);
-
-      return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-    },
-
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
-};
-
-var TraversalRules = {
-  Simple: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_MENUITEM,
-     Ci.nsIAccessibleRole.ROLE_LINK,
-     Ci.nsIAccessibleRole.ROLE_PAGETAB,
-     Ci.nsIAccessibleRole.ROLE_GRAPHIC,
-     // XXX: Find a better solution for ROLE_STATICTEXT.
-     // It allows to filter list bullets but at the same time it
-     // filters CSS generated content too as an unwanted side effect.
-     // Ci.nsIAccessibleRole.ROLE_STATICTEXT,
-     Ci.nsIAccessibleRole.ROLE_TEXT_LEAF,
-     Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
-     Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
-     Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
-     Ci.nsIAccessibleRole.ROLE_COMBOBOX,
-     Ci.nsIAccessibleRole.ROLE_PROGRESSBAR,
-     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
-     Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
-     Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
-     Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
-     Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
-     Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
-     Ci.nsIAccessibleRole.ROLE_ENTRY],
-    function Simple_match(aAccessible) {
-      switch (aAccessible.role) {
-      case Ci.nsIAccessibleRole.ROLE_COMBOBOX:
-        // We don't want to ignore the subtree because this is often
-        // where the list box hangs out.
-        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-      case Ci.nsIAccessibleRole.ROLE_TEXT_LEAF:
-        {
-          // Nameless text leaves are boring, skip them.
-          let name = aAccessible.name;
-          if (name && name.trim())
-            return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-          else
-            return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
-        }
-      case Ci.nsIAccessibleRole.ROLE_LINK:
-        // If the link has children we should land on them instead.
-        // Image map links don't have children so we need to match those.
-        if (aAccessible.childCount == 0)
-          return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-        else
-          return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
-      default:
-        // Ignore the subtree, if there is one. So that we don't land on
-        // the same content that was already presented by its parent.
-        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
-          Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
-      }
-    }
-  ),
-
-  Anchor: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_LINK],
-    function Anchor_match(aAccessible)
-    {
-      // We want to ignore links, only focus named anchors.
-      let state = {};
-      let extraState = {};
-      aAccessible.getState(state, extraState);
-      if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
-        return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
-      } else {
-        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-      }
-    }),
-
-  Button: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
-     Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
-     Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
-     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
-     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID]),
-
-  Combobox: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_COMBOBOX,
-     Ci.nsIAccessibleRole.ROLE_LISTBOX]),
-
-  Entry: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_ENTRY,
-     Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT]),
-
-  FormElement: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
-     Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
-     Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
-     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
-     Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID,
-     Ci.nsIAccessibleRole.ROLE_COMBOBOX,
-     Ci.nsIAccessibleRole.ROLE_LISTBOX,
-     Ci.nsIAccessibleRole.ROLE_ENTRY,
-     Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
-     Ci.nsIAccessibleRole.ROLE_PAGETAB,
-     Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
-     Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
-     Ci.nsIAccessibleRole.ROLE_SLIDER,
-     Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
-     Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM]),
-
-  Graphic: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_GRAPHIC]),
-
-  Heading: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_HEADING]),
-
-  ListItem: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_LISTITEM,
-     Ci.nsIAccessibleRole.ROLE_TERM]),
-
-  Link: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_LINK],
-    function Link_match(aAccessible)
-    {
-      // We want to ignore anchors, only focus real links.
-      let state = {};
-      let extraState = {};
-      aAccessible.getState(state, extraState);
-      if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
-        return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
-      } else {
-        return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
-      }
-    }),
-
-  List: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_LIST,
-     Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST]),
-
-  PageTab: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_PAGETAB]),
-
-  RadioButton: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
-     Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM]),
-
-  Separator: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_SEPARATOR]),
-
-  Table: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_TABLE]),
-
-  Checkbox: new BaseTraversalRule(
-    [Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
-     Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM])
-};
-
-var VirtualCursorController = {
-  exploreByTouch: false,
-  editableState: 0,
-
-  attach: function attach(aWindow) {
-    this.chromeWin = aWindow;
-    this.chromeWin.document.addEventListener('keypress', this, true);
-    this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
-  },
-
-  detach: function detach() {
-    this.chromeWin.document.removeEventListener('keypress', this, true);
-    this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
-  },
-
-  handleEvent: function VirtualCursorController_handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case 'keypress':
-        this._handleKeypress(aEvent);
-        break;
-      case 'mozAccessFuGesture':
-        this._handleGesture(aEvent);
-        break;
-    }
-  },
-
-  _handleGesture: function _handleGesture(aEvent) {
-    let document = Utils.getCurrentContentDoc(this.chromeWin);
-    let detail = aEvent.detail;
-    Logger.info('Gesture', detail.type,
-                '(fingers: ' + detail.touches.length + ')');
-
-    if (detail.touches.length == 1) {
-      switch (detail.type) {
-        case 'swiperight':
-          this.moveForward(document, aEvent.shiftKey);
-          break;
-        case 'swipeleft':
-          this.moveBackward(document, aEvent.shiftKey);
-          break;
-        case 'doubletap':
-          this.activateCurrent(document);
-          break;
-        case 'explore':
-          this.moveToPoint(document, detail.x, detail.y);
-          break;
-      }
-    }
-
-    if (detail.touches.length == 3) {
-      switch (detail.type) {
-        case 'swiperight':
-          if (!Utils.scroll(this.chromeWin, -1, true))
-            Utils.changePage(this.chromeWin, -1);
-          break;
-        case 'swipedown':
-          Utils.scroll(this.chromeWin, -1);
-          break;
-        case 'swipeleft':
-          if (!Utils.scroll(this.chromeWin, 1, true))
-            Utils.changePage(this.chromeWin, 1);
-        case 'swipeup':
-          Utils.scroll(this.chromeWin, 1);
-          break;
-      }
-    }
-  },
-
-  _handleKeypress: function _handleKeypress(aEvent) {
-    let document = Utils.getCurrentContentDoc(this.chromeWin);
-    let target = aEvent.target;
-
-    // Ignore keys with modifiers so the content could take advantage of them.
-    if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
-      return;
-
-    switch (aEvent.keyCode) {
-      case 0:
-        // an alphanumeric key was pressed, handle it separately.
-        // If it was pressed with either alt or ctrl, just pass through.
-        // If it was pressed with meta, pass the key on without the meta.
-        if (this.editableState)
-          return;
-
-        let key = String.fromCharCode(aEvent.charCode);
-        let methodName = '', rule = {};
-        try {
-          [methodName, rule] = this.keyMap[key];
-        } catch (x) {
-          return;
-        }
-        this[methodName](document, false, rule);
-        break;
-      case aEvent.DOM_VK_RIGHT:
-        if (this.editableState) {
-          if (target.selectionEnd != target.textLength)
-            // Don't move forward if caret is not at end of entry.
-            // XXX: Fix for rtl
-            return;
-          else
-            target.blur();
-        }
-        this.moveForward(document, aEvent.shiftKey);
-        break;
-      case aEvent.DOM_VK_LEFT:
-        if (this.editableState) {
-          if (target.selectionEnd != 0)
-            // Don't move backward if caret is not at start of entry.
-            // XXX: Fix for rtl
-            return;
-          else
-            target.blur();
-        }
-        this.moveBackward(document, aEvent.shiftKey);
-        break;
-      case aEvent.DOM_VK_UP:
-        if (this.editableState & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) {
-          if (target.selectionEnd != 0)
-            // Don't blur content if caret is not at start of text area.
-            return;
-          else
-            target.blur();
-        }
-
-        if (Utils.MozBuildApp == 'mobile/android')
-          // Return focus to native Android browser chrome.
-          Cc['@mozilla.org/android/bridge;1'].
-            getService(Ci.nsIAndroidBridge).handleGeckoMessage(
-              JSON.stringify({ gecko: { type: 'ToggleChrome:Focus' } }));
-        break;
-      case aEvent.DOM_VK_RETURN:
-      case aEvent.DOM_VK_ENTER:
-        if (this.editableState)
-          return;
-        this.activateCurrent(document);
-        break;
-      default:
-        return;
-    }
-
-    aEvent.preventDefault();
-    aEvent.stopPropagation();
-  },
-
-  moveToPoint: function moveToPoint(aDocument, aX, aY) {
-    Utils.getVirtualCursor(aDocument).moveToPoint(TraversalRules.Simple,
-                                                  aX, aY, true);
-  },
-
-  moveForward: function moveForward(aDocument, aLast, aRule) {
-    let virtualCursor = Utils.getVirtualCursor(aDocument);
-    if (aLast) {
-      virtualCursor.moveLast(TraversalRules.Simple);
-    } else {
-      try {
-        virtualCursor.moveNext(aRule || TraversalRules.Simple);
-      } catch (x) {
-        this.moveCursorToObject(
-          virtualCursor,
-          Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
-      }
-    }
-  },
-
-  moveBackward: function moveBackward(aDocument, aFirst, aRule) {
-    let virtualCursor = Utils.getVirtualCursor(aDocument);
-    if (aFirst) {
-      virtualCursor.moveFirst(TraversalRules.Simple);
-    } else {
-      try {
-        virtualCursor.movePrevious(aRule || TraversalRules.Simple);
-      } catch (x) {
-        this.moveCursorToObject(
-          virtualCursor,
-          Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
-      }
-    }
-  },
-
-  activateCurrent: function activateCurrent(document) {
-    let virtualCursor = Utils.getVirtualCursor(document);
-    let acc = virtualCursor.position;
-
-    if (acc.actionCount > 0) {
-      acc.doAction(0);
-    } else {
-      // XXX Some mobile widget sets do not expose actions properly
-      // (via ARIA roles, etc.), so we need to generate a click.
-      // Could possibly be made simpler in the future. Maybe core
-      // engine could expose nsCoreUtiles::DispatchMouseEvent()?
-      let docAcc = Utils.AccRetrieval.getAccessibleFor(this.chromeWin.document);
-      let docX = {}, docY = {}, docW = {}, docH = {};
-      docAcc.getBounds(docX, docY, docW, docH);
-
-      let objX = {}, objY = {}, objW = {}, objH = {};
-      acc.getBounds(objX, objY, objW, objH);
-
-      let x = Math.round((objX.value - docX.value) + objW.value / 2);
-      let y = Math.round((objY.value - docY.value) + objH.value / 2);
-
-      let cwu = this.chromeWin.QueryInterface(Ci.nsIInterfaceRequestor).
-        getInterface(Ci.nsIDOMWindowUtils);
-      cwu.sendMouseEventToWindow('mousedown', x, y, 0, 1, 0, false);
-      cwu.sendMouseEventToWindow('mouseup', x, y, 0, 1, 0, false);
-    }
-  },
-
-  moveCursorToObject: function moveCursorToObject(aVirtualCursor,
-                                                  aAccessible, aRule) {
-    aVirtualCursor.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
-  },
-
-  keyMap: {
-    a: ['moveForward', TraversalRules.Anchor],
-    A: ['moveBackward', TraversalRules.Anchor],
-    b: ['moveForward', TraversalRules.Button],
-    B: ['moveBackward', TraversalRules.Button],
-    c: ['moveForward', TraversalRules.Combobox],
-    C: ['moveBackward', TraversalRules.Combobox],
-    e: ['moveForward', TraversalRules.Entry],
-    E: ['moveBackward', TraversalRules.Entry],
-    f: ['moveForward', TraversalRules.FormElement],
-    F: ['moveBackward', TraversalRules.FormElement],
-    g: ['moveForward', TraversalRules.Graphic],
-    G: ['moveBackward', TraversalRules.Graphic],
-    h: ['moveForward', TraversalRules.Heading],
-    H: ['moveBackward', TraversalRules.Heading],
-    i: ['moveForward', TraversalRules.ListItem],
-    I: ['moveBackward', TraversalRules.ListItem],
-    k: ['moveForward', TraversalRules.Link],
-    K: ['moveBackward', TraversalRules.Link],
-    l: ['moveForward', TraversalRules.List],
-    L: ['moveBackward', TraversalRules.List],
-    p: ['moveForward', TraversalRules.PageTab],
-    P: ['moveBackward', TraversalRules.PageTab],
-    r: ['moveForward', TraversalRules.RadioButton],
-    R: ['moveBackward', TraversalRules.RadioButton],
-    s: ['moveForward', TraversalRules.Separator],
-    S: ['moveBackward', TraversalRules.Separator],
-    t: ['moveForward', TraversalRules.Table],
-    T: ['moveBackward', TraversalRules.Table],
-    x: ['moveForward', TraversalRules.Checkbox],
-    X: ['moveBackward', TraversalRules.Checkbox]
-  }
-};
new file mode 100644
--- /dev/null
+++ b/accessible/src/jsat/content-script.js
@@ -0,0 +1,253 @@
+/* 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/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import('resource://gre/modules/accessibility/EventManager.jsm');
+Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+
+Logger.debug('content-script.js');
+
+function virtualCursorControl(aMessage) {
+  if (Logger.logLevel >= Logger.DEBUG)
+    Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
+
+  try {
+    let vc = Utils.getVirtualCursor(content.document);
+    let origin = aMessage.json.origin;
+    if (origin != 'child') {
+      if (forwardMessage(vc, aMessage))
+        return;
+    }
+
+    let details = aMessage.json;
+    let rule = TraversalRules[details.rule];
+    let moved = 0;
+    switch (details.action) {
+    case 'moveFirst':
+    case 'moveLast':
+      moved = vc[details.action](rule);
+      break;
+    case 'moveNext':
+    case 'movePrevious':
+      try {
+        if (origin == 'parent' && vc.position == null) {
+          if (details.action == 'moveNext')
+            moved = vc.moveFirst(rule);
+          else
+            moved = vc.moveLast(rule);
+        } else {
+          moved = vc[details.action](rule);
+        }
+      } catch (x) {
+        moved = vc.moveNext(rule, content.document.activeElement, true);
+      }
+      break;
+    case 'moveToPoint':
+      moved = vc.moveToPoint(rule, details.x, details.y, true);
+      break;
+    case 'presentLastPivot':
+      EventManager.presentLastPivot();
+      break;
+    default:
+      break;
+    }
+
+    if (moved == true) {
+      forwardMessage(vc, aMessage);
+    } else if (moved == false && details.action != 'moveToPoint') {
+      if (origin == 'parent') {
+        vc.position = null;
+      }
+      aMessage.json.origin = 'child';
+      sendAsyncMessage('AccessFu:VirtualCursor', aMessage.json);
+    }
+  } catch (x) {
+    Logger.error(x);
+  }
+}
+
+function forwardMessage(aVirtualCursor, aMessage) {
+  try {
+    let acc = aVirtualCursor.position;
+    if (acc && acc.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
+      let mm = Utils.getMessageManager(acc.DOMNode);
+      mm.addMessageListener(aMessage.name, virtualCursorControl);
+      aMessage.json.origin = 'parent';
+      // XXX: OOP content's screen offset is 0,
+      // so we remove the real screen offset here.
+      aMessage.json.x -= content.mozInnerScreenX;
+      aMessage.json.y -= content.mozInnerScreenY;
+      mm.sendAsyncMessage(aMessage.name, aMessage.json);
+      return true;
+    }
+  } catch (x) {
+    Logger.error(x);
+  }
+  return false;
+}
+
+function activateCurrent(aMessage) {
+  Logger.debug('activateCurrent');
+  function activateAccessible(aAccessible) {
+    if (aAccessible.actionCount > 0) {
+      aAccessible.doAction(0);
+    } else {
+      // XXX Some mobile widget sets do not expose actions properly
+      // (via ARIA roles, etc.), so we need to generate a click.
+      // Could possibly be made simpler in the future. Maybe core
+      // engine could expose nsCoreUtiles::DispatchMouseEvent()?
+      let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
+      let docX = {}, docY = {}, docW = {}, docH = {};
+      docAcc.getBounds(docX, docY, docW, docH);
+
+      let objX = {}, objY = {}, objW = {}, objH = {};
+      aAccessible.getBounds(objX, objY, objW, objH);
+
+      let x = Math.round((objX.value - docX.value) + objW.value / 2);
+      let y = Math.round((objY.value - docY.value) + objH.value / 2);
+
+      let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).
+        getInterface(Ci.nsIDOMWindowUtils);
+      cwu.sendMouseEventToWindow('mousedown', x, y, 0, 1, 0, false);
+      cwu.sendMouseEventToWindow('mouseup', x, y, 0, 1, 0, false);
+    }
+  }
+
+  let vc = Utils.getVirtualCursor(content.document);
+  if (!forwardMessage(vc, aMessage))
+    activateAccessible(vc.position);
+}
+
+function scroll(aMessage) {
+  let vc = Utils.getVirtualCursor(content.document);
+
+  function tryToScroll() {
+    let horiz = aMessage.json.horizontal;
+    let page = aMessage.json.page;
+
+    // Search up heirarchy for scrollable element.
+    let acc = vc.position;
+    while (acc) {
+      let elem = acc.DOMNode;
+
+      // We will do window scrolling next.
+      if (elem == content.document)
+        break;
+
+      if (!horiz && elem.clientHeight < elem.scrollHeight) {
+        let s = content.getComputedStyle(elem);
+        if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
+          elem.scrollTop += page * elem.clientHeight;
+          return true;
+        }
+      }
+
+      if (horiz) {
+        if (elem.clientWidth < elem.scrollWidth) {
+          let s = content.getComputedStyle(elem);
+          if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
+            elem.scrollLeft += page * elem.clientWidth;
+            return true;
+          }
+        }
+
+        let controllers = acc.
+          getRelationByType(
+            Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
+        for (let i = 0; controllers.targetsCount > i; i++) {
+          let controller = controllers.getTarget(i);
+          // If the section has a controlling slider, it should be considered
+          // the page-turner.
+          if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
+            // Sliders are controlled with ctrl+right/left. I just decided :)
+            let evt = content.document.createEvent('KeyboardEvent');
+            evt.initKeyEvent(
+              'keypress', true, true, null,
+              true, false, false, false,
+              (page > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
+            controller.DOMNode.dispatchEvent(evt);
+            return true;
+          }
+        }
+      }
+      acc = acc.parent;
+    }
+
+    // Scroll window.
+    if (!horiz && content.scrollMaxY &&
+        ((page > 0 && content.scrollY < content.scrollMaxY) ||
+         (page < 0 && content.scrollY > 0))) {
+      content.scroll(0, content.innerHeight);
+      return true;
+    } else if (horiz && content.scrollMaxX &&
+               ((page > 0 && content.scrollX < content.scrollMaxX) ||
+                (page < 0 && content.scrollX > 0))) {
+      content.scroll(content.innerWidth, 0);
+      return true;
+    }
+
+    return false;
+  }
+
+  if (aMessage.json.origin != 'child') {
+    if (forwardMessage(vc, aMessage))
+      return;
+  }
+
+  if (!tryToScroll()) {
+    // Failed to scroll anything in this document. Try in parent document.
+    aMessage.json.origin = 'child';
+    sendAsyncMessage('AccessFu:Scroll', aMessage.json);
+  }
+}
+
+addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
+addMessageListener('AccessFu:Activate', activateCurrent);
+addMessageListener('AccessFu:Scroll', scroll);
+
+addMessageListener(
+  'AccessFu:Start',
+  function(m) {
+    if (m.json.buildApp)
+      Utils.MozBuildApp = m.json.buildApp;
+
+    EventManager.start(
+      function sendMessage(aName, aDetails) {
+        sendAsyncMessage(aName, aDetails);
+      });
+
+    docShell.QueryInterface(Ci.nsIInterfaceRequestor).
+      getInterface(Ci.nsIWebProgress).
+      addProgressListener(EventManager,
+                          (Ci.nsIWebProgress.NOTIFY_STATE_ALL |
+                           Ci.nsIWebProgress.NOTIFY_LOCATION));
+    addEventListener('scroll', EventManager, true);
+    addEventListener('resize', EventManager, true);
+    // XXX: Ideally this would be an a11y event. Bug #742280.
+    addEventListener('DOMActivate', EventManager, true);
+  });
+
+addMessageListener(
+  'AccessFu:Stop',
+  function(m) {
+    Logger.debug('AccessFu:Stop');
+
+    EventManager.stop();
+
+    docShell.QueryInterface(Ci.nsIInterfaceRequestor).
+      getInterface(Ci.nsIWebProgress).
+      removeProgressListener(EventManager);
+    removeEventListener('scroll', EventManager, true);
+    removeEventListener('resize', EventManager, true);
+    // XXX: Ideally this would be an a11y event. Bug #742280.
+    removeEventListener('DOMActivate', EventManager, true);
+  });
+
+sendAsyncMessage('AccessFu:Ready');
--- a/accessible/src/jsat/jar.mn
+++ b/accessible/src/jsat/jar.mn
@@ -1,6 +1,7 @@
 # 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/.
 
 toolkit.jar:
     content/global/accessibility/AccessFu.css (AccessFu.css)
+    content/global/accessibility/content-script.js (content-script.js)
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -185,16 +185,19 @@ var shell = {
     SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
       Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
     });
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
 
     ppmm.addMessageListener("content-handler", this);
+    ppmm.addMessageListener("dial-handler", this);
+    ppmm.addMessageListener("sms-handler", this);
+    ppmm.addMessageListener("mail-handler", this);
   },
 
   stop: function shell_stop() {
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
@@ -389,27 +392,28 @@ var shell = {
       url: msg.uri,
       manifestURL: msg.manifest,
       isActivity: (msg.type == 'activity'),
       target: msg.target
     });
   },
 
   receiveMessage: function shell_receiveMessage(message) {
-    if (message.name != 'content-handler') {
+    var names = { 'content-handler': 'view',
+                  'dial-handler'   : 'dial',
+                  'mail-handler'   : 'new',
+                  'sms-handler'    : 'new' }
+
+    if (!(message.name in names))
       return;
-    }
-    let handler = message.json;
+
+    let data = message.data;
     new MozActivity({
-      name: 'view',
-      data: {
-        type: handler.type,
-        url: handler.url,
-        extras: handler.extras
-      }
+      name: names[message.name],
+      data: data
     });
   }
 };
 
 function nsBrowserAccess() {
 }
 
 nsBrowserAccess.prototype = {
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -37,15 +37,27 @@ category app-startup ProcessGlobal servi
 # ContentHandler.js
 component {d18d0216-d50c-11e1-ba54-efb18d0ef0ac} ContentHandler.js
 contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {d18d0216-d50c-11e1-ba54-efb18d0ef0ac}
 
 # PaymentGlue.js
 component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
 contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
 
+# TelProtocolHandler.js
+component {782775dd-7351-45ea-aff1-0ffa872cfdd2} TelProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=tel {782775dd-7351-45ea-aff1-0ffa872cfdd2}
+
+# SmsProtocolHandler.js
+component {81ca20cb-0dad-4e32-8566-979c8998bd73} SmsProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=sms {81ca20cb-0dad-4e32-8566-979c8998bd73}
+
+# MailtoProtocolHandler.js
+component {50777e53-0331-4366-a191-900999be386c} MailtoProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=mailto {50777e53-0331-4366-a191-900999be386c}
+
 # YoutubeProtocolHandler.js
 component {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad} YoutubeProtocolHandler.js
 contract @mozilla.org/network/protocol;1?name=vnd.youtube {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}
 
 # RecoveryService.js
 component {b3caca5d-0bb0-48c6-912b-6be6cbf08832} RecoveryService.js
 contract @mozilla.org/recovery-service;1 {b3caca5d-0bb0-48c6-912b-6be6cbf08832}
new file mode 100644
--- /dev/null
+++ b/b2g/components/MailtoProtocolHandler.js
@@ -0,0 +1,46 @@
+/* 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";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+function MailtoProtocolHandler() {
+}
+
+MailtoProtocolHandler.prototype = {
+
+  scheme: "mailto",
+  defaultPort: -1,
+  protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
+                 Ci.nsIProtocolHandler.URI_NOAUTH |
+                 Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
+                 Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
+  allowPort: function() false,
+
+  newURI: function Proto_newURI(aSpec, aOriginCharset) {
+    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
+    uri.spec = aSpec;
+    return uri;
+  },
+
+  newChannel: function Proto_newChannel(aURI) {
+    cpmm.sendAsyncMessage("mail-handler", {
+      URI: aURI.spec,
+      type: "mail" });
+
+    throw Components.results.NS_ERROR_ILLEGAL_VALUE;
+  },
+
+  classID: Components.ID("{50777e53-0331-4366-a191-900999be386c}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
+};
+
+let NSGetFactory = XPCOMUtils.generateNSGetFactory([MailtoProtocolHandler]);
--- a/b2g/components/Makefile.in
+++ b/b2g/components/Makefile.in
@@ -18,20 +18,31 @@ XPIDLSRCS = \
 
 EXTRA_PP_COMPONENTS = \
         ActivitiesGlue.js \
         AlertsService.js \
         B2GComponents.manifest \
         ContentHandler.js \
         ContentPermissionPrompt.js \
         DirectoryProvider.js \
+        MailtoProtocolHandler.js \
         MozKeyboard.js \
         ProcessGlobal.js \
         PaymentGlue.js \
+        SmsProtocolHandler.js \
+        TelProtocolHandler.js \
         YoutubeProtocolHandler.js \
         RecoveryService.js \
         $(NULL)
 
+EXTRA_JS_MODULES = \
+	TelURIParser.jsm \
+	$(NULL)
+
+TEST_DIRS = \
+	test \
+	$(NULL)
+
 ifdef MOZ_UPDATER
 EXTRA_PP_COMPONENTS += UpdatePrompt.js
 endif
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/b2g/components/SmsProtocolHandler.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/**
+ * SmsProtocolHandle.js
+ *
+ * This file implements the URLs for SMS
+ * https://www.rfc-editor.org/rfc/rfc5724.txt
+ */
+
+"use strict";
+
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource:///modules/TelURIParser.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+function SmsProtocolHandler() {
+}
+
+SmsProtocolHandler.prototype = {
+
+  scheme: "sms",
+  defaultPort: -1,
+  protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
+                 Ci.nsIProtocolHandler.URI_NOAUTH |
+                 Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
+                 Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
+  allowPort: function() false,
+
+  newURI: function Proto_newURI(aSpec, aOriginCharset) {
+    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
+    uri.spec = aSpec;
+    return uri;
+  },
+
+  newChannel: function Proto_newChannel(aURI) {
+    let number = TelURIParser.parseURI('sms', aURI.spec);
+
+    if (number) {
+      cpmm.sendAsyncMessage("sms-handler", {
+        number: number,
+        type: "websms/sms" });
+    }
+
+    throw Components.results.NS_ERROR_ILLEGAL_VALUE;
+  },
+
+  classID: Components.ID("{81ca20cb-0dad-4e32-8566-979c8998bd73}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
+};
+
+let NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsProtocolHandler]);
new file mode 100644
--- /dev/null
+++ b/b2g/components/TelProtocolHandler.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+/**
+ * TelProtocolHandle.js
+ *
+ * This file implements the URLs for Telephone Calls
+ * https://www.ietf.org/rfc/rfc2806.txt
+ */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource:///modules/TelURIParser.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
+function TelProtocolHandler() {
+}
+
+TelProtocolHandler.prototype = {
+
+  scheme: "tel",
+  defaultPort: -1,
+  protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
+                 Ci.nsIProtocolHandler.URI_NOAUTH |
+                 Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
+                 Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
+  allowPort: function() false,
+
+  newURI: function Proto_newURI(aSpec, aOriginCharset) {
+    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
+    uri.spec = aSpec;
+    return uri;
+  },
+
+  newChannel: function Proto_newChannel(aURI) {
+    let number = TelURIParser.parseURI('tel', aURI.spec);
+
+    if (number) {
+      cpmm.sendAsyncMessage("dial-handler", {
+        number: number,
+        type: "webtelephony/number" });
+    }
+
+    throw Components.results.NS_ERROR_ILLEGAL_VALUE;
+  },
+
+  classID: Components.ID("{782775dd-7351-45ea-aff1-0ffa872cfdd2}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
+};
+
+let NSGetFactory = XPCOMUtils.generateNSGetFactory([TelProtocolHandler]);
new file mode 100644
--- /dev/null
+++ b/b2g/components/TelURIParser.jsm
@@ -0,0 +1,120 @@
+/* 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";
+
+let EXPORTED_SYMBOLS = ["TelURIParser"];
+
+/**
+ * Singleton providing functionality for parsing tel: and sms: URIs
+ */
+let TelURIParser = {
+  parseURI: function(scheme, uri) {
+    // Ignore MWI and USSD codes. See 794034.
+    if (uri.indexOf('*') != -1 || uri.indexOf('#') != -1) {
+      return null;
+    }
+
+    // https://www.ietf.org/rfc/rfc2806.txt
+    let subscriber = uri.slice((scheme + ':').length);
+
+    if (!subscriber.length) {
+      return null;
+    }
+
+    let number = '';
+    let pos = 0;
+    let len = subscriber.length;
+
+    // visual-separator
+    let visualSeparator = [ '-', '.', '(', ')' ];
+    let digits = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
+    let dtmfDigits = [ '*', '#', 'A', 'B', 'C', 'D' ];
+    let pauseCharacter = [ 'p', 'w' ];
+
+    // global-phone-number
+    if (subscriber[pos] == '+') {
+      number += '+';
+      for (++pos; pos < len; ++pos) {
+        if (visualSeparator.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else if (digits.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else {
+          break;
+        }
+      }
+    }
+    // local-phone-number
+    else {
+      for (; pos < len; ++pos) {
+        if (visualSeparator.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else if (digits.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else if (dtmfDigits.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else if (pauseCharacter.indexOf(subscriber[pos]) != -1) {
+          number += subscriber[pos];
+        } else {
+          break;
+        }
+      }
+
+      // this means error
+      if (!number.length) {
+        return null;
+      }
+
+      // isdn-subaddress
+      if (subscriber.substring(pos, pos + 6) == ';isub=') {
+        let subaddress = '';
+
+        for (pos += 6; pos < len; ++pos) {
+          if (visualSeparator.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else if (digits.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else {
+            break;
+          }
+        }
+
+        // FIXME: ignore subaddress - Bug 795242
+      }
+
+      // post-dial
+      if (subscriber.substring(pos, pos + 7) == ';postd=') {
+        let subaddress = '';
+
+        for (pos += 7; pos < len; ++pos) {
+          if (visualSeparator.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else if (digits.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else if (dtmfDigits.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else if (pauseCharacter.indexOf(subscriber[pos]) != -1) {
+            subaddress += subscriber[pos];
+          } else {
+            break;
+          }
+        }
+
+        // FIXME: ignore subaddress - Bug 795242
+      }
+
+      // area-specific
+      if (subscriber.substring(pos, pos + 15) == ';phone-context=') {
+        pos += 15;
+
+        // global-network-prefix | local-network-prefix | private-prefi
+        number = subscriber.substring(pos, subscriber.length) + number;
+      }
+    }
+
+    return number || null;
+  }
+};
+
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/Makefile.in
@@ -0,0 +1,20 @@
+# vim: noexpandtab ts=8 sw=8
+#
+# 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/.
+
+DEPTH		= @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir = @relativesrcdir@
+FAIL_ON_WARNINGS := 1
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = B2GComponents
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_bug793310.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+  Components.utils.import("resource:///modules/TelURIParser.jsm")
+
+  // global-phone-number
+   do_check_eq(TelURIParser.parseURI('tel', 'tel:+1234'), '+1234');
+
+  // global-phone-number => ignored chars
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:+1234_123'), '+1234');
+
+  // global-phone-number => visualSeparator + digits
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:+-.()1234567890'), '+-.()1234567890');
+
+  // local-phone-number
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:1234'), '1234');
+
+  // local-phone-number => visualSeparator + digits + dtmfDigits + pauseCharacter
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:-.()1234567890ABCDpw'), '-.()1234567890ABCDpw');
+
+  // local-phone-number => visualSeparator + digits + dtmfDigits + pauseCharacter + ignored chars
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:-.()1234567890ABCDpw_'), '-.()1234567890ABCDpw');
+
+  // local-phone-number => isdn-subaddress
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:123;isub=123'), '123');
+
+  // local-phone-number => post-dial
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:123;postd=123'),  '123');
+
+  // local-phone-number => prefix
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:123;phone-context=+0321'), '+0321123');
+
+  // local-phone-number => isdn-subaddress + post-dial + prefix
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:123;isub=123;postd=123;phone-context=+0321'), '+0321123');
+}
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_bug793310.js]
--- a/browser/base/content/test/browser_tab_dragdrop2_frame1.xul
+++ b/browser/base/content/test/browser_tab_dragdrop2_frame1.xul
@@ -47,17 +47,16 @@ function test_panels()
   addEventListener("popuphidden", nextTest, false);
   return nextTest();
 }
 
 function nextTest()
 {
   ok(true,"popuphidden " + i)
   if (i == tests.length) {
-    SimpleTest.finish();
     return i;
   }
 
   currentTest = tests[i];
   var panel = createPanel(currentTest.attrs);
   currentTest.test(panel);
   return i;
 }
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -102,16 +102,18 @@ MOCHITEST_BROWSER_FILES = \
 	browser_webconsole_bug_651501_document_body_autocomplete.js \
 	browser_webconsole_bug_653531_highlighter_console_helper.js \
 	browser_webconsole_bug_659907_console_dir.js \
 	browser_webconsole_bug_664131_console_group.js \
 	browser_webconsole_bug_704295.js \
 	browser_webconsole_bug_658368_time_methods.js \
 	browser_webconsole_bug_764572_output_open_url.js \
 	browser_webconsole_bug_622303_persistent_filters.js \
+        browser_webconsole_bug_770099_bad_policyuri.js \
+        browser_webconsole_bug_770099_violation.js \
 	browser_webconsole_window_zombie.js \
 	browser_cached_messages.js \
 	browser_bug664688_sandbox_update_after_navigation.js \
 	browser_webconsole_menustatus.js \
 	browser_result_format_as_string.js \
 	browser_webconsole_bug_737873_mixedcontent.js \
 	browser_output_breaks_after_console_dir_uninspectable.js \
 	browser_console_log_inspectable_object.js \
@@ -183,13 +185,17 @@ MOCHITEST_BROWSER_FILES += \
 	test-bug-585956-console-trace.html \
 	test-bug-644419-log-limits.html \
 	test-bug-632275-getters.html \
 	test-bug-646025-console-file-location.html \
 	test-file-location.js \
 	test-bug-658368-time-methods.html \
 	test-webconsole-error-observer.html \
 	test-for-of.html \
+        test_bug_770099_violation.html \
+        test_bug_770099_violation.html^headers^ \
+        test_bug_770099_bad_policy_uri.html \
+        test_bug_770099_bad_policy_uri.html^headers^ \
 	test-result-format-as-string.html \
 	test-bug-737873-mixedcontent.html \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_770099_bad_policyuri.js
@@ -0,0 +1,55 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// Tests that the Web Console CSP messages are displayed
+
+const TEST_BAD_POLICY_URI = "https://example.com/browser/browser/devtools/webconsole/test/test_bug_770099_bad_policy_uri.html";
+
+let hud = undefined;
+
+function test() {
+  addTab("data:text/html;charset=utf8,Web Console CSP bad policy URI test");
+  browser.addEventListener("load", function _onLoad() {
+    browser.removeEventListener("load", _onLoad, true);
+    openConsole(null, loadDocument);
+  }, true);
+}
+
+function loadDocument(theHud) {
+  hud = theHud;
+  hud.jsterm.clearOutput();
+  browser.addEventListener("load", onLoad, true);
+  content.location = TEST_BAD_POLICY_URI;
+}
+
+function onLoad(aEvent) {
+  browser.removeEventListener("load", onLoad, true);
+  testPolicyURIMessage();
+}
+
+function testPolicyURIMessage() {
+  let aOutputNode = hud.outputNode;
+ 
+  waitForSuccess(
+    {
+      name: "CSP policy URI warning displayed successfully",
+      validatorFn: function() {
+        return aOutputNode.querySelector(".webconsole-msg-error");
+      },
+
+      successFn: function() {
+        //tests on the urlnode
+        let node = aOutputNode.querySelector(".webconsole-msg-error");
+        isnot(node.textContent.indexOf("can't fetch policy"), -1,
+                                       "CSP Policy URI message found");
+        finishTest();
+      },
+
+      failureFn: finishTest,
+    }
+  );
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_770099_violation.js
@@ -0,0 +1,55 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// Tests that the Web Console CSP messages are displayed
+
+const TEST_VIOLATION = "https://example.com/browser/browser/devtools/webconsole/test/test_bug_770099_violation.html";
+
+let hud = undefined;
+
+function test() {
+  addTab("data:text/html;charset=utf8,Web Console CSP violation test");
+  browser.addEventListener("load", function _onLoad() {
+    browser.removeEventListener("load", _onLoad, true);
+    openConsole(null, loadDocument);
+  }, true);
+}
+
+function loadDocument(theHud){
+  hud = theHud;
+  hud.jsterm.clearOutput()
+  browser.addEventListener("load", onLoad, true);
+  content.location = TEST_VIOLATION;
+}
+
+function onLoad(aEvent) {
+  browser.removeEventListener("load", onLoad, true);
+  testViolationMessage();
+}
+
+function testViolationMessage(){
+  let aOutputNode = hud.outputNode;
+
+  waitForSuccess(
+    {
+      name: "CSP policy URI warning displayed successfully",
+      validatorFn: function() {
+        return aOutputNode.querySelector(".webconsole-msg-warn");
+      },
+
+      successFn: function() {
+        //tests on the urlnode
+        let node = aOutputNode.querySelector(".webconsole-msg-warn");
+        isnot(node.textContent.indexOf("violated"), -1,
+                                       "CSP violation message found");
+        finishTest();
+      },
+
+      failureFn: finishTest,
+    }
+  );
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_bug_770099_bad_policy_uri.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Test for Bug 770099 - bad policy-uri</title>
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_bug_770099_bad_policy_uri.html^headers^
@@ -0,0 +1,2 @@
+X-Content-Security-Policy: policy-uri http://example.com/some_policy
+Content-type: text/html; charset=utf-8
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_bug_770099_violation.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Test for Bug 770099 - policy violation</title>
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a>
+<img src="http://some.example.com/test.png">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test_bug_770099_violation.html^headers^
@@ -0,0 +1,1 @@
+X-Content-Security-Policy: default-src 'self'
--- a/build/unix/build-clang/build-clang.py
+++ b/build/unix/build-clang/build-clang.py
@@ -102,16 +102,17 @@ isDarwin = platform.system() == "Darwin"
 
 def build_one_stage_aux(stage_dir, is_stage_one):
     os.mkdir(stage_dir)
 
     build_dir = stage_dir + "/build"
     inst_dir = stage_dir + "/clang"
 
     configure_opts = ["--enable-optimized",
+                      "--enable-targets=x86,x86_64,arm",
                       "--disable-assertions",
                       "--prefix=%s" % inst_dir,
                       "--with-gcc-toolchain=/tools/gcc-4.5-0moz3"]
     if is_stage_one and not isDarwin:
         configure_opts.append("--with-optimize-option=-O0")
 
     build_package(llvm_source_dir, build_dir, configure_opts)
 
@@ -124,16 +125,18 @@ if not os.path.exists(source_dir):
            llvm_source_dir, llvm_revision)
     svn_co("http://llvm.org/svn/llvm-project/cfe/trunk",
            clang_source_dir, llvm_revision)
     svn_co("http://llvm.org/svn/llvm-project/compiler-rt/trunk",
            compiler_rt_source_dir, llvm_revision)
     os.symlink("../../clang", llvm_source_dir + "/tools/clang")
     os.symlink("../../compiler-rt", llvm_source_dir + "/projects/compiler-rt")
     patch("llvm-debug-frame.patch", 1, llvm_source_dir)
+    patch("llvm-deterministic.patch", 1, llvm_source_dir)
+    patch("clang-deterministic.patch", 1, clang_source_dir)
     if not isDarwin:
         patch("old-ld-hack.patch", 1, llvm_source_dir)
         patch("compiler-rt-gnu89-inline.patch", 0, compiler_rt_source_dir)
         patch("no-sse-on-linux.patch", 1, clang_source_dir)
 
 if os.path.exists(build_dir):
     shutil.rmtree(build_dir)
 os.makedirs(build_dir)
new file mode 100644
--- /dev/null
+++ b/build/unix/build-clang/clang-deterministic.patch
@@ -0,0 +1,45 @@
+diff --git a/include/clang/AST/CXXInheritance.h b/include/clang/AST/CXXInheritance.h
+index ee6eba7..87bdbe0 100644
+--- a/include/clang/AST/CXXInheritance.h
++++ b/include/clang/AST/CXXInheritance.h
+@@ -19,7 +19,7 @@
+ #include "clang/AST/DeclCXX.h"
+ #include "clang/AST/Type.h"
+ #include "clang/AST/TypeOrdering.h"
+-#include "llvm/ADT/DenseMap.h"
++#include "llvm/ADT/MapVector.h"
+ #include "llvm/ADT/SmallSet.h"
+ #include "llvm/ADT/SmallVector.h"
+ #include <list>
+@@ -271,15 +271,14 @@ struct UniqueVirtualMethod {
+ /// pair is the virtual method that overrides it (including the
+ /// subobject in which that virtual function occurs).
+ class OverridingMethods {
+-  llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> > 
+-    Overrides;
++  typedef SmallVector<UniqueVirtualMethod, 4> ValuesT;
++  typedef llvm::MapVector<unsigned, ValuesT> MapType;
++  MapType Overrides;
+ 
+ public:
+   // Iterate over the set of subobjects that have overriding methods.
+-  typedef llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> >
+-            ::iterator iterator;
+-  typedef llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> >
+-            ::const_iterator const_iterator;
++  typedef MapType::iterator iterator;
++  typedef MapType::const_iterator const_iterator;
+   iterator begin() { return Overrides.begin(); }
+   const_iterator begin() const { return Overrides.begin(); }
+   iterator end() { return Overrides.end(); }
+@@ -357,8 +356,8 @@ public:
+ /// 0 represents the virtua base class subobject of that type, while
+ /// subobject numbers greater than 0 refer to non-virtual base class
+ /// subobjects of that type.
+-class CXXFinalOverriderMap 
+-  : public llvm::DenseMap<const CXXMethodDecl *, OverridingMethods> { };
++class CXXFinalOverriderMap
++  : public llvm::MapVector<const CXXMethodDecl *, OverridingMethods> { };
+ 
+ /// \brief A set of all the primary bases for a class.
+ class CXXIndirectPrimaryBaseSet
new file mode 100644
--- /dev/null
+++ b/build/unix/build-clang/llvm-deterministic.patch
@@ -0,0 +1,86 @@
+diff --git a/include/llvm/ADT/MapVector.h b/include/llvm/ADT/MapVector.h
+new file mode 100644
+index 0000000..bad207b
+--- /dev/null
++++ b/include/llvm/ADT/MapVector.h
+@@ -0,0 +1,80 @@
++//===- llvm/ADT/MapVector.h - Map with deterministic value order *- C++ -*-===//
++//
++//                     The LLVM Compiler Infrastructure
++//
++// This file is distributed under the University of Illinois Open Source
++// License. See LICENSE.TXT for details.
++//
++//===----------------------------------------------------------------------===//
++//
++// This file implements a map that provides insertion order iteration. The
++// interface is purposefully minimal. The key is assumed to be cheap to copy
++// and 2 copies are kept, one for indexing in a DenseMap, one for iteration in
++// a std::vector.
++//
++//===----------------------------------------------------------------------===//
++
++#ifndef LLVM_ADT_MAPVECTOR_H
++#define LLVM_ADT_MAPVECTOR_H
++
++#include "llvm/ADT/ArrayRef.h"
++#include "llvm/ADT/DenseMap.h"
++#include <vector>
++
++namespace llvm {
++
++/// This class implements a map that also provides access to all stored values
++/// in a deterministic order. The values are kept in a std::vector and the
++/// mapping is done with DenseMap from Keys to indexes in that vector.
++template<typename KeyT, typename ValueT>
++class MapVector {
++  typedef llvm::DenseMap<KeyT, unsigned> MapType;
++  typedef std::vector<std::pair<KeyT, ValueT> > VectorType;
++  typedef typename VectorType::size_type SizeType;
++
++  MapType Map;
++  VectorType Vector;
++
++public:
++  typedef typename VectorType::iterator iterator;
++  typedef typename VectorType::const_iterator const_iterator;
++
++  SizeType size() const {
++    return Vector.size();
++  }
++
++  iterator begin() {
++    return Vector.begin();
++  }
++
++  const_iterator begin() const {
++    return Vector.begin();
++  }
++
++  iterator end() {
++    return Vector.end();
++  }
++
++  const_iterator end() const {
++    return Vector.end();
++  }
++
++  bool empty() const {
++    return Vector.empty();
++  }
++
++  ValueT &operator[](const KeyT &Key) {
++    std::pair<KeyT, unsigned> Pair = std::make_pair(Key, 0);
++    std::pair<typename MapType::iterator, bool> Result = Map.insert(Pair);
++    unsigned &I = Result.first->second;
++    if (Result.second) {
++      Vector.push_back(std::make_pair(Key, ValueT()));
++      I = Vector.size() - 1;
++    }
++    return Vector[I].second;
++  }
++};
++
++}
++
++#endif
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -74,18 +74,18 @@ class ImageLoader;
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x57fe44ae, 0x6656, 0x44b8, \
-  { 0x8d, 0xc0, 0xfc, 0xa7, 0x43, 0x28, 0xbe, 0x86 } }
+{ 0x0e1324c9, 0xc997, 0x447e, \
+  { 0xbc, 0xd9, 0xa6, 0x57, 0x80, 0x29, 0x91, 0xe4 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -1616,17 +1616,20 @@ public:
    * to save memory. The PresShell informs the document whether its images
    * should be locked or not via SetImageLockingState().
    *
    * See bug 512260.
    */
 
   // Add/Remove images from the document image tracker
   virtual nsresult AddImage(imgIRequest* aImage) = 0;
-  virtual nsresult RemoveImage(imgIRequest* aImage) = 0;
+  // If the REQUEST_DISCARD flag is passed then if the lock count is zero we
+  // will request the image be discarded now (instead of waiting).
+  enum { REQUEST_DISCARD = 0x1 };
+  virtual nsresult RemoveImage(imgIRequest* aImage, uint32_t aFlags = 0) = 0;
 
   // Makes the images on this document locked/unlocked. By default, the locking
   // state is unlocked/false.
   virtual nsresult SetImageLockingState(bool aLocked) = 0;
 
   virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin) = 0;
   virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin) = 0;
   virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) = 0;
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -6,36 +6,36 @@
  * Content Security Policy Utilities
  *
  * Overview
  * This contains a set of classes and utilities for CSP.  It is in this
  * separate file for testing purposes.
  */
 
 const Cu = Components.utils;
+const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
 // Module stuff
 var EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", "CSPHost",
-                        "CSPWarning", "CSPError", "CSPdebug",
-                        "CSPViolationReportListener", "CSPLocalizer"];
+                        "CSPdebug", "CSPViolationReportListener", "CSPLocalizer"];
 
 var STRINGS_URI = "chrome://global/locale/security/csp.properties";
 
 // these are not exported
 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
-                 .getService(Components.interfaces.nsIIOService);
+                 .getService(Ci.nsIIOService);
 
 var gETLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
-                   .getService(Components.interfaces.nsIEffectiveTLDService);
+                   .getService(Ci.nsIEffectiveTLDService);
 
 // These regexps represent the concrete syntax on the w3 spec as of 7-5-2012
 // scheme          = <scheme production from RFC 3986>
 const R_SCHEME     = new RegExp ("([a-zA-Z0-9\\-]+)", 'i');
 const R_GETSCHEME  = new RegExp ("^" + R_SCHEME.source + "(?=\\:)", 'i');
 
 // scheme-source   = scheme ":"
 const R_SCHEMESRC  = new RegExp ("^" + R_SCHEME.source + "\\:$", 'i');
@@ -71,100 +71,72 @@ var gPrefObserver = {
   get debugEnabled () {
     if (!this._branch)
       this._initialize();
     return this._debugEnabled;
   },
 
   _initialize: function() {
     var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
-                    .getService(Components.interfaces.nsIPrefService);
+                    .getService(Ci.nsIPrefService);
     this._branch = prefSvc.getBranch("security.csp.");
     this._branch.addObserver("", this, false);
     this._debugEnabled = this._branch.getBoolPref("debug");
   },
 
   unregister: function() {
-    if(!this._branch) return;
+    if (!this._branch) return;
     this._branch.removeObserver("", this);
   },
 
   observe: function(aSubject, aTopic, aData) {
-    if(aTopic != "nsPref:changed") return;
-    if(aData === "debug")
+    if (aTopic != "nsPref:changed") return;
+    if (aData === "debug")
       this._debugEnabled = this._branch.getBoolPref("debug");
   },
-
 };
 
-
-function CSPWarning(aMsg, aWindowID, aSource, aScriptSample, aLineNum) {
-  var textMessage = 'CSP WARN:  ' + aMsg + "\n";
-
-  var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
-                    .createInstance(Components.interfaces.nsIScriptError);
-  consoleMsg.initWithWindowID(textMessage, aSource, aScriptSample, aLineNum, 0,
-                  Components.interfaces.nsIScriptError.warningFlag,
-                  "Content Security Policy", aWindowID);
-  Components.classes["@mozilla.org/consoleservice;1"]
-                    .getService(Components.interfaces.nsIConsoleService)
-                    .logMessage(consoleMsg);
-}
-
-function CSPError(aMsg, aWindowID) {
-  var textMessage = 'CSP ERROR:  ' + aMsg + "\n";
-
-  var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
-                    .createInstance(Components.interfaces.nsIScriptError);
-  consoleMsg.initWithWindowID(textMessage, null, null, 0, 0,
-                  Components.interfaces.nsIScriptError.errorFlag,
-                  "Content Security Policy", aWindowID);
-  Components.classes["@mozilla.org/consoleservice;1"]
-                    .getService(Components.interfaces.nsIConsoleService)
-                    .logMessage(consoleMsg);
-}
-
 function CSPdebug(aMsg) {
   if (!gPrefObserver.debugEnabled) return;
 
   aMsg = 'CSP debug: ' + aMsg + "\n";
   Components.classes["@mozilla.org/consoleservice;1"]
-                    .getService(Components.interfaces.nsIConsoleService)
+                    .getService(Ci.nsIConsoleService)
                     .logStringMessage(aMsg);
 }
 
 // Callback to resume a request once the policy-uri has been fetched
 function CSPPolicyURIListener(policyURI, docRequest, csp) {
   this._policyURI = policyURI;    // location of remote policy
   this._docRequest = docRequest;  // the parent document request
   this._csp = csp;                // parent document's CSP
   this._policy = "";              // contents fetched from policyURI
   this._wrapper = null;           // nsIScriptableInputStream
-  this._docURI = docRequest.QueryInterface(Components.interfaces.nsIChannel)
+  this._docURI = docRequest.QueryInterface(Ci.nsIChannel)
                  .URI;    // parent document URI (to be used as 'self')
 }
 
 CSPPolicyURIListener.prototype = {
 
   QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIStreamListener) ||
-        iid.equals(Components.interfaces.nsIRequestObserver) ||
-        iid.equals(Components.interfaces.nsISupports))
+    if (iid.equals(Ci.nsIStreamListener) ||
+        iid.equals(Ci.nsIRequestObserver) ||
+        iid.equals(Ci.nsISupports))
       return this;
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
   onStartRequest:
   function(request, context) {},
 
   onDataAvailable:
   function(request, context, inputStream, offset, count) {
     if (this._wrapper == null) {
       this._wrapper = Components.classes["@mozilla.org/scriptableinputstream;1"]
-                      .createInstance(Components.interfaces.nsIScriptableInputStream);
+                      .createInstance(Ci.nsIScriptableInputStream);
       this._wrapper.init(inputStream);
     }
     // store the remote policy as it becomes available
     this._policy += this._wrapper.read(count);
   },
 
   onStopRequest:
   function(request, context, status) {
@@ -236,83 +208,88 @@ CSPRep.ALLOW_DIRECTIVE   = "allow";
   * @returns
   *        an instance of CSPRep
   */
 CSPRep.fromString = function(aStr, self, docRequest, csp) {
   var SD = CSPRep.SRC_DIRECTIVES;
   var UD = CSPRep.URI_DIRECTIVES;
   var aCSPR = new CSPRep();
   aCSPR._originalText = aStr;
+  aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
 
   var selfUri = null;
-  if (self instanceof Components.interfaces.nsIURI)
+  if (self instanceof Ci.nsIURI)
     selfUri = self.clone();
 
   var dirs = aStr.split(";");
 
   directive:
   for each(var dir in dirs) {
     dir = dir.trim();
     if (dir.length < 1) continue;
 
     var dirname = dir.split(/\s+/)[0];
     var dirvalue = dir.substring(dirname.length).trim();
 
     if (aCSPR._directives.hasOwnProperty(dirname)) {
       // Check for (most) duplicate directives
-      CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
+      cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
+                                                [dirname]));
       CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
       continue directive;
     }
 
     // OPTIONS DIRECTIVE ////////////////////////////////////////////////
     if (dirname === CSPRep.OPTIONS_DIRECTIVE) {
       if (aCSPR._allowInlineScripts || aCSPR._allowEval) {
         // Check for duplicate options directives
-        CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
+        cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
+                                                  [dirname]));
         CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
         continue directive;
       }
 
       // grab value tokens and interpret them
       var options = dirvalue.split(/\s+/);
       for each (var opt in options) {
         if (opt === "inline-script")
           aCSPR._allowInlineScripts = true;
         else if (opt === "eval-script")
           aCSPR._allowEval = true;
         else
-          CSPWarning(CSPLocalizer.getFormatStr("doNotUnderstandOption", [opt]));
+          cspWarn(aCSPR, CSPLocalizer.getFormatStr("doNotUnderstandOption",
+                                                   [opt]));
       }
       continue directive;
     }
 
     // ALLOW DIRECTIVE //////////////////////////////////////////////////
     // parse "allow" as equivalent to "default-src", at least until the spec
     // stabilizes, at which time we can stop parsing "allow"
     if (dirname === CSPRep.ALLOW_DIRECTIVE) {
-      CSPWarning(CSPLocalizer.getStr("allowDirectiveDeprecated"));
+      cspWarn(aCSPR, CSPLocalizer.getStr("allowDirectiveDeprecated"));
       if (aCSPR._directives.hasOwnProperty(SD.DEFAULT_SRC)) {
         // Check for duplicate default-src and allow directives
-        CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
+        cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
+                                                  [dirname]));
         CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
         continue directive;
       }
-      var dv = CSPSourceList.fromString(dirvalue, self, true);
+      var dv = CSPSourceList.fromString(dirvalue, aCSPR, self, true);
       if (dv) {
         aCSPR._directives[SD.DEFAULT_SRC] = dv;
         continue directive;
       }
     }
 
     // SOURCE DIRECTIVES ////////////////////////////////////////////////
     for each(var sdi in SD) {
       if (dirname === sdi) {
         // process dirs, and enforce that 'self' is defined.
-        var dv = CSPSourceList.fromString(dirvalue, self, true);
+        var dv = CSPSourceList.fromString(dirvalue, aCSPR, self, true);
         if (dv) {
           aCSPR._directives[sdi] = dv;
           continue directive;
         }
       }
     }
 
     // REPORT URI ///////////////////////////////////////////////////////
@@ -335,116 +312,124 @@ CSPRep.fromString = function(aStr, self,
           uri.host;
 
           // Verify that each report URI is in the same etld + 1 and that the
           // scheme and port match "self" if "self" is defined, and just that
           // it's valid otherwise.
           if (self) {
             if (gETLDService.getBaseDomain(uri) !==
                 gETLDService.getBaseDomain(selfUri)) {
-              CSPWarning(CSPLocalizer.getFormatStr("notETLDPlus1",
-                         [gETLDService.getBaseDomain(uri)]));
+              cspWarn(aCSPR,
+                      CSPLocalizer.getFormatStr("notETLDPlus1",
+                                            [gETLDService.getBaseDomain(uri)]));
               continue;
             }
             if (!uri.schemeIs(selfUri.scheme)) {
-              CSPWarning(CSPLocalizer.getFormatStr("notSameScheme",
-                         [uri.asciiSpec]));
+              cspWarn(aCSPR, CSPLocalizer.getFormatStr("notSameScheme",
+                                                       [uri.asciiSpec]));
               continue;
             }
             if (uri.port && uri.port !== selfUri.port) {
-              CSPWarning(CSPLocalizer.getFormatStr("notSamePort",
-                         [uri.asciiSpec]));
+              cspWarn(aCSPR, CSPLocalizer.getFormatStr("notSamePort",
+                                                       [uri.asciiSpec]));
               continue;
             }
           }
         } catch(e) {
           switch (e.result) {
             case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
             case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
               if (uri.host !== selfUri.host) {
-                CSPWarning(CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
-                         [selfUri.host, uri.host]));
+                cspWarn(aCSPR,
+                        CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
+                                                  [selfUri.host, uri.host]));
                 continue;
               }
               break;
 
             default:
-              CSPWarning(CSPLocalizer.getFormatStr("couldNotParseReportURI",
-                        [uriStrings[i]]));
+              cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotParseReportURI",
+                                                       [uriStrings[i]]));
               continue;
           }
         }
         // all verification passed: same ETLD+1, scheme, and port.
         okUriStrings.push(uri.asciiSpec);
       }
       aCSPR._directives[UD.REPORT_URI] = okUriStrings.join(' ');
       continue directive;
     }
 
     // POLICY URI //////////////////////////////////////////////////////////
     if (dirname === UD.POLICY_URI) {
       // POLICY_URI can only be alone
       if (aCSPR._directives.length > 0 || dirs.length > 1) {
-        CSPError(CSPLocalizer.getStr("policyURINotAlone"));
+        cspError(aCSPR, CSPLocalizer.getStr("policyURINotAlone"));
         return CSPRep.fromString("default-src 'none'");
       }
       // if we were called without a reference to the parent document request
       // we won't be able to suspend it while we fetch the policy -> fail closed
       if (!docRequest || !csp) {
-        CSPError(CSPLocalizer.getStr("noParentRequest"));
+        cspError(aCSPR, CSPLocalizer.getStr("noParentRequest"));
         return CSPRep.fromString("default-src 'none'");
       }
 
       var uri = '';
       try {
         uri = gIoService.newURI(dirvalue, null, selfUri);
       } catch(e) {
-        CSPError(CSPLocalizer.getFormatStr("policyURIParseError", [dirvalue]));
+        cspError(aCSPR, CSPLocalizer.getFormatStr("policyURIParseError",
+                                                  [dirvalue]));
         return CSPRep.fromString("default-src 'none'");
       }
 
       // Verify that policy URI comes from the same origin
       if (selfUri) {
-        if (selfUri.host !== uri.host){
-          CSPError(CSPLocalizer.getFormatStr("nonMatchingHost", [uri.host]));
+        if (selfUri.host !== uri.host) {
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingHost",
+                                                    [uri.host]));
           return CSPRep.fromString("default-src 'none'");
         }
-        if (selfUri.port !== uri.port){
-          CSPError(CSPLocalizer.getFormatStr("nonMatchingPort", [uri.port.toString()]));
+        if (selfUri.port !== uri.port) {
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingPort",
+                                                    [uri.port.toString()]));
           return CSPRep.fromString("default-src 'none'");
         }
-        if (selfUri.scheme !== uri.scheme){
-          CSPError(CSPLocalizer.getFormatStr("nonMatchingScheme", [uri.scheme]));
+        if (selfUri.scheme !== uri.scheme) {
+          cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingScheme",
+                                                    [uri.scheme]));
           return CSPRep.fromString("default-src 'none'");
         }
       }
 
       // suspend the parent document request while we fetch the policy-uri
       try {
         docRequest.suspend();
         var chan = gIoService.newChannel(uri.asciiSpec, null, null);
         // make request anonymous (no cookies, etc.) so the request for the
         // policy-uri can't be abused for CSRF
-        chan.loadFlags |= Components.interfaces.nsIChannel.LOAD_ANONYMOUS;
+        chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
         chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
       }
       catch (e) {
         // resume the document request and apply most restrictive policy
         docRequest.resume();
-        CSPError(CSPLocalizer.getFormatStr("errorFetchingPolicy", [e.toString()]));
+        cspError(aCSPR, CSPLocalizer.getFormatStr("errorFetchingPolicy",
+                                                  [e.toString()]));
         return CSPRep.fromString("default-src 'none'");
       }
 
       // return a fully-open policy to be intersected with the contents of the
       // policy-uri when it returns
       return CSPRep.fromString("default-src *");
     }
 
     // UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
-    CSPWarning(CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective", [dirname]));
+    cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective",
+                                             [dirname]));
 
   } // end directive: loop
 
   // if makeExplicit fails for any reason, default to default-src 'none'.  This
   // includes the case where "default-src" is not present.
   if (aCSPR.makeExplicit())
     return aCSPR;
   return CSPRep.fromString("default-src 'none'", self);
@@ -506,17 +491,17 @@ CSPRep.prototype = {
    */
   permits:
   function csp_permits(aURI, aContext) {
     if (!aURI) return false;
 
     // GLOBALLY ALLOW "about:" SCHEME
     if (aURI instanceof String && aURI.substring(0,6) === "about:")
       return true;
-    if (aURI instanceof Components.interfaces.nsIURI && aURI.scheme === "about")
+    if (aURI instanceof Ci.nsIURI && aURI.scheme === "about")
       return true;
 
     // make sure the context is valid
     for (var i in CSPRep.SRC_DIRECTIVES) {
       if (CSPRep.SRC_DIRECTIVES[i] === aContext) {
         return this._directives[aContext].permits(aURI);
       }
     }
@@ -558,43 +543,46 @@ CSPRep.prototype = {
       newRep._directives[reportURIDir] = aCSPRep._directives[reportURIDir].concat();
     }
 
     newRep._allowEval =          this.allowsEvalInScripts
                            && aCSPRep.allowsEvalInScripts;
 
     newRep._allowInlineScripts = this.allowsInlineScripts
                            && aCSPRep.allowsInlineScripts;
+ 
+    newRep._innerWindowID = this._innerWindowID ?
+                              this._innerWindowID : aCSPRep._innerWindowID;
 
     return newRep;
   },
 
   /**
    * Copies default source list to each unspecified directive.
    * @returns
    *      true  if the makeExplicit succeeds
    *      false if it fails (for some weird reason)
    */
   makeExplicit:
   function cspsd_makeExplicit() {
     var SD = CSPRep.SRC_DIRECTIVES;
     var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
     if (!defaultSrcDir) {
-      CSPWarning(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
+      this.warn(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
       return false;
     }
 
     for (var dir in SD) {
       var dirv = SD[dir];
       if (dirv === SD.DEFAULT_SRC) continue;
       if (!this._directives[dirv]) {
         // implicit directive, make explicit.
         // All but frame-ancestors directive inherit from 'allow' (bug 555068)
         if (dirv === SD.FRAME_ANCESTORS)
-          this._directives[dirv] = CSPSourceList.fromString("*");
+          this._directives[dirv] = CSPSourceList.fromString("*",this);
         else
           this._directives[dirv] = defaultSrcDir.clone();
         this._directives[dirv]._isImplicit = true;
       }
     }
     this._isInitialized = true;
     return true;
   },
@@ -608,16 +596,72 @@ CSPRep.prototype = {
 
   /**
    * Returns true if inline scripts are enabled through the "inline"
    * keyword.
    */
   get allowsInlineScripts () {
     return this._allowInlineScripts;
   },
+
+  /**
+   * Sends a warning message to the error console and web developer console.
+   * @param aMsg
+   *        The message to send
+   * @param aSource (optional)
+   *        The URL of the file in which the error occurred
+   * @param aScriptLine (optional)
+   *        The line in the source file which the error occurred
+   * @param aLineNum (optional)
+   *        The number of the line where the error occurred
+   */
+  warn:
+  function cspd_warn(aMsg, aSource, aScriptLine, aLineNum) {
+    var textMessage = 'CSP WARN:  ' + aMsg + "\n";
+
+    var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
+                               .createInstance(Ci.nsIScriptError);
+    if (this._innerWindowID) {
+      consoleMsg.initWithWindowID(textMessage, aSource, aScriptLine, aLineNum,
+                                  0, Ci.nsIScriptError.warningFlag,
+                                  "Content Security Policy",
+                                  this._innerWindowID);
+    } else {
+      consoleMsg.init(textMessage, aSource, aScriptLine, aLineNum, 0,
+                      Ci.nsIScriptError.warningFlag,
+                      "Content Security Policy");
+    }
+    Components.classes["@mozilla.org/consoleservice;1"]
+              .getService(Ci.nsIConsoleService).logMessage(consoleMsg);
+  },
+
+  /**
+   * Sends an error message to the error console and web developer console.
+   * @param aMsg
+   *        The message to send
+   */
+  error:
+  function cspsd_error(aMsg) {
+    var textMessage = 'CSP ERROR:  ' + aMsg + "\n";
+
+    var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
+                               .createInstance(Ci.nsIScriptError);
+    if (this._innerWindowID) {
+      consoleMsg.initWithWindowID(textMessage, null, null, 0, 0,
+                                  Ci.nsIScriptError.errorFlag,
+                                  "Content Security Policy",
+                                  this._innerWindowID);
+    }
+    else {
+      consoleMsg.init(textMessage, null, null, 0, 0,
+                      Ci.nsIScriptError.errorFlag, "Content Security Policy");
+    }
+    Components.classes["@mozilla.org/consoleservice;1"]
+              .getService(Ci.nsIConsoleService).logMessage(consoleMsg);
+  },
 };
 
 //////////////////////////////////////////////////////////////////////
 /**
  * Class to represent a list of sources
  */
 function CSPSourceList() {
   this._sources = [];
@@ -628,57 +672,63 @@ function CSPSourceList() {
   this._isImplicit = false;
 }
 
 /**
  * Factory to create a new CSPSourceList, parsed from a string.
  *
  * @param aStr
  *        string rep of a CSP Source List
+ * @param aCSPRep
+ *        the CSPRep to which this souce list belongs. If null, CSP errors and
+ *        warnings will not be sent to the web console.
  * @param self (optional)
  *        URI or CSPSource representing the "self" source
  * @param enforceSelfChecks (optional)
  *        if present, and "true", will check to be sure "self" has the
  *        appropriate values to inherit when they are omitted from the source.
  * @returns
  *        an instance of CSPSourceList
  */
-CSPSourceList.fromString = function(aStr, self, enforceSelfChecks) {
+CSPSourceList.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
   // source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
   //             / *WSP "'none'" *WSP
 
   /* If self parameter is passed, convert to CSPSource,
      unless it is already a CSPSource. */
-  if(self && !(self instanceof CSPSource)) {
-     self = CSPSource.create(self);
+  if (self && !(self instanceof CSPSource)) {
+     self = CSPSource.create(self, aCSPRep);
   }
 
   var slObj = new CSPSourceList();
+  slObj._CSPRep = aCSPRep;
   aStr = aStr.trim();
   // w3 specifies case insensitive equality
-  if (aStr.toUpperCase() === "'NONE'"){
+  if (aStr.toUpperCase() === "'NONE'") {
     slObj._permitAllSources = false;
     return slObj;
   }
 
   var tokens = aStr.split(/\s+/);
   for (var i in tokens) {
-    if (!R_SOURCEEXP.test(tokens[i])){
-      CSPWarning(CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
-                                           [tokens[i]]));
+    if (!R_SOURCEEXP.test(tokens[i])) {
+      cspWarn(aCSPRep,
+              CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
+                                        [tokens[i]]));
       continue;
     }
-    var src = CSPSource.create(tokens[i], self, enforceSelfChecks);
+    var src = CSPSource.create(tokens[i], aCSPRep, self, enforceSelfChecks);
     if (!src) {
-      CSPWarning(CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
-                                           [tokens[i]]));
+      cspWarn(aCSPRep,
+              CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
+                                        [tokens[i]]));
       continue;
     }
     // if a source is a *, then we can permit all sources
-    if (src.permitAll){
+    if (src.permitAll) {
       slObj._permitAllSources = true;
       return slObj;
     } else {
       slObj._sources.push(src);
     }
   }
 
   return slObj;
@@ -753,16 +803,17 @@ CSPSourceList.prototype = {
    * Makes a new deep copy of this object.
    * @returns
    *      a new CSPSourceList
    */
   clone:
   function() {
     var aSL = new CSPSourceList();
     aSL._permitAllSources = this._permitAllSources;
+    aSL._CSPRep = this._CSPRep;
     for (var i in this._sources) {
       aSL._sources[i] = this._sources[i].clone();
     }
     return aSL;
   },
 
   /**
    * Determines if this directive accepts a URI.
@@ -795,17 +846,17 @@ CSPSourceList.prototype = {
   intersectWith:
   function cspsd_intersectWith(that) {
 
     var newCSPSrcList = null;
 
     if (!that) return this.clone();
 
     if (this.isNone() || that.isNone())
-      newCSPSrcList = CSPSourceList.fromString("'none'");
+      newCSPSrcList = CSPSourceList.fromString("'none'", this._CSPRep);
 
     if (this.isAll()) newCSPSrcList = that.clone();
     if (that.isAll()) newCSPSrcList = this.clone();
 
     if (!newCSPSrcList) {
       // the shortcuts didn't apply, must do intersection the hard way.
       // --  find only common sources
 
@@ -832,16 +883,19 @@ CSPSourceList.prototype = {
       }
       newCSPSrcList = new CSPSourceList();
       newCSPSrcList._sources = isrcs;
     }
 
     // if either was explicit, so is this.
     newCSPSrcList._isImplicit = this._isImplicit && that._isImplicit;
 
+    if ((!newCSPSrcList._CSPRep) && that._CSPRep) {
+      newCSPSrcList._CSPRep = that._CSPRep;
+    }
     return newCSPSrcList;
   }
 }
 
 //////////////////////////////////////////////////////////////////////
 /**
  * Class to model a source (scheme, host, port)
  */
@@ -860,30 +914,33 @@ function CSPSource() {
 /**
  * General factory method to create a new source from one of the following
  * types:
  *  - nsURI
  *  - string
  *  - CSPSource (clone)
  * @param aData
  *        string, nsURI, or CSPSource
+ * @param aCSPRep
+ *        The CSPRep this source belongs to. If null, CSP errors and warnings
+ *        will not be sent to the web console.
  * @param self (optional)
  *	  if present, string, URI, or CSPSource representing the "self" resource
  * @param enforceSelfChecks (optional)
  *	  if present, and "true", will check to be sure "self" has the
  *        appropriate values to inherit when they are omitted from the source.
  * @returns
  *        an instance of CSPSource
  */
-CSPSource.create = function(aData, self, enforceSelfChecks) {
+CSPSource.create = function(aData, aCSPRep, self, enforceSelfChecks) {
   if (typeof aData === 'string')
-    return CSPSource.fromString(aData, self, enforceSelfChecks);
+    return CSPSource.fromString(aData, aCSPRep, self, enforceSelfChecks);
 
-  if (aData instanceof Components.interfaces.nsIURI)
-    return CSPSource.fromURI(aData, self, enforceSelfChecks);
+  if (aData instanceof Ci.nsIURI)
+    return CSPSource.fromURI(aData, aCSPRep, self, enforceSelfChecks);
 
   if (aData instanceof CSPSource) {
     var ns = aData.clone();
     ns._self = CSPSource.create(self);
     return ns;
   }
 
   return null;
@@ -891,51 +948,56 @@ CSPSource.create = function(aData, self,
 
 /**
  * Factory to create a new CSPSource, from a nsIURI.
  *
  * Don't use this if you want to wildcard ports!
  *
  * @param aURI
  *        nsIURI rep of a URI
+ * @param aCSPRep
+ *        The policy this source belongs to. If null, CSP errors and warnings
+ *        will not be sent to the web console.
  * @param self (optional)
  *        string or CSPSource representing the "self" source
  * @param enforceSelfChecks (optional)
  *        if present, and "true", will check to be sure "self" has the
  *        appropriate values to inherit when they are omitted from aURI.
  * @returns
  *        an instance of CSPSource
  */
-CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
-  if (!(aURI instanceof Components.interfaces.nsIURI)){
-    CSPError(CSPLocalizer.getStr("cspSourceNotURI"));
+CSPSource.fromURI = function(aURI, aCSPRep, self, enforceSelfChecks) {
+  if (!(aURI instanceof Ci.nsIURI)) {
+    cspError(aCSPRep, CSPLocalizer.getStr("cspSourceNotURI"));
     return null;
   }
 
   if (!self && enforceSelfChecks) {
-    CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
+    cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided"));
     return null;
   }
 
   if (self && !(self instanceof CSPSource)) {
-    self = CSPSource.create(self, undefined, false);
+    self = CSPSource.create(self, aCSPRep, undefined, false);
   }
 
   var sObj = new CSPSource();
   sObj._self = self;
+  sObj._CSPRep = aCSPRep;
 
   // PARSE
   // If 'self' is undefined, then use default port for scheme if there is one.
 
   // grab scheme (if there is one)
   try {
     sObj._scheme = aURI.scheme;
   } catch(e) {
     sObj._scheme = undefined;
-    CSPError(CSPLocalizer.getFormatStr("uriWithoutScheme", [aURI.asciiSpec]));
+    cspError(aCSPRep, CSPLocalizer.getFormatStr("uriWithoutScheme",
+                                                [aURI.asciiSpec]));
     return null;
   }
 
   // grab host (if there is one)
   try {
     // if there's no host, an exception will get thrown
     // (NS_ERROR_FAILURE)
     sObj._host = CSPHost.fromString(aURI.host);
@@ -968,53 +1030,57 @@ CSPSource.fromURI = function(aURI, self,
   return sObj;
 };
 
 /**
  * Factory to create a new CSPSource, parsed from a string.
  *
  * @param aStr
  *        string rep of a CSP Source
+ * @param aCSPRep
+ *        the CSPRep this CSPSource belongs to
  * @param self (optional)
  *        string, URI, or CSPSource representing the "self" source
  * @param enforceSelfChecks (optional)
  *        if present, and "true", will check to be sure "self" has the
  *        appropriate values to inherit when they are omitted from aURI.
  * @returns
  *        an instance of CSPSource
  */
-CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
+CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
   if (!aStr)
     return null;
 
   if (!(typeof aStr === 'string')) {
-    CSPError(CSPLocalizer.getStr("argumentIsNotString"));
+    cspError(aCSPRep, CSPLocalizer.getStr("argumentIsNotString"));
     return null;
   }
 
   var sObj = new CSPSource();
   sObj._self = self;
+  sObj._CSPRep = aCSPRep;
+
 
   // if equal, return does match
-  if (aStr === "*"){
+  if (aStr === "*") {
     sObj._permitAll = true;
     return sObj;
   }
 
   if (!self && enforceSelfChecks) {
-    CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
+    cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided"));
     return null;
   }
 
   if (self && !(self instanceof CSPSource)) {
-    self = CSPSource.create(self, undefined, false);
+    self = CSPSource.create(self, aCSPRep, undefined, false);
   }
 
   // check for scheme-source match
-  if (R_SCHEMESRC.test(aStr)){
+  if (R_SCHEMESRC.test(aStr)) {
     var schemeSrcMatch = R_GETSCHEME.exec(aStr);
     sObj._scheme = schemeSrcMatch[0];
     if (!sObj._host) sObj._host = CSPHost.fromString("*");
     if (!sObj._port) sObj._port = "*";
     return sObj;
   }
 
   // check for host-source or ext-host-source match
@@ -1026,52 +1092,56 @@ CSPSource.fromString = function(aStr, se
       sObj._scheme = self.scheme;
       schemeMatch = null;
     } else {
       sObj._scheme = schemeMatch[0];
     }
 
     // get array of matches to the R_HOST regular expression
     var hostMatch = R_HOST.exec(aStr);
-    if (!hostMatch){
-      CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource", [aStr]));
+    if (!hostMatch) {
+      cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
+                                                  [aStr]));
       return null;
     }
     // host regex gets scheme, so remove scheme from aStr. Add 3 for '://'
     if (schemeMatch)
       hostMatch = R_HOST.exec(aStr.substring(schemeMatch[0].length + 3));
     sObj._host = CSPHost.fromString(hostMatch[0]);
     var portMatch = R_PORT.exec(aStr);
     if (!portMatch) {
       // gets the default port for the given scheme
       defPort = Services.io.getProtocolHandler(sObj._scheme).defaultPort;
       if (!defPort) {
-        CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource", [aStr]));
+        cspError(aCSPRep,
+                 CSPLocalizer.getFormatStr("couldntParseInvalidSource",
+                                           [aStr]));
         return null;
       }
       sObj._port = defPort;
     }
     else {
       // strip the ':' from the port
       sObj._port = portMatch[0].substr(1);
     }
     return sObj;
   }
 
   // check for 'self' (case insensitive)
-  if (aStr.toUpperCase() === "'SELF'"){
-    if (!self){
-      CSPError(CSPLocalizer.getStr("selfKeywordNoSelfData"));
+  if (aStr.toUpperCase() === "'SELF'") {
+    if (!self) {
+      cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData"));
       return null;
     }
     sObj._self = self.clone();
     sObj._isSelf = true;
     return sObj;
   }
-  CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource",[aStr]));
+  cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
+                                              [aStr]));
   return null;
 };
 
 CSPSource.validSchemeName = function(aStr) {
   // <scheme-name>       ::= <alpha><scheme-suffix>
   // <scheme-suffix>     ::= <scheme-chr>
   //                      | <scheme-suffix><scheme-chr>
   // <scheme-chr>        ::= <letter> | <digit> | "+" | "." | "-"
@@ -1151,32 +1221,33 @@ CSPSource.prototype = {
   clone:
   function() {
     var aClone = new CSPSource();
     aClone._self = this._self ? this._self.clone() : undefined;
     aClone._scheme = this._scheme;
     aClone._port = this._port;
     aClone._host = this._host ? this._host.clone() : undefined;
     aClone._isSelf = this._isSelf;
+    aClone._CSPRep = this._CSPRep;
     return aClone;
   },
 
   /**
    * Determines if this Source accepts a URI.
    * @param aSource
    *        the URI, or CSPSource in question
    * @returns
    *        true if the URI matches a source in this source list.
    */
   permits:
   function(aSource) {
     if (!aSource) return false;
 
     if (!(aSource instanceof CSPSource))
-      return this.permits(CSPSource.create(aSource));
+      return this.permits(CSPSource.create(aSource, this._CSPRep));
 
     // verify scheme
     if (this.scheme != aSource.scheme)
       return false;
 
     // port is defined in 'this' (undefined means it may not be relevant
     // to the scheme) AND this port (implicit or explicit) matches
     // aSource's port
@@ -1220,55 +1291,65 @@ CSPSource.prototype = {
       newSource._port = this.port;
     else if (this.port === "*")
       newSource._port = that.port;
     else if (that.port === "*")
       newSource._port = this.port;
     else if (that.port === this.port)
       newSource._port = this.port;
     else {
-      CSPError(CSPLocalizer.getFormatStr("notIntersectPort", [this.toString(), that.toString()]));
+      let msg = CSPLocalizer.getFormatStr("notIntersectPort",
+                                          [this.toString(), that.toString()]);
+      cspError(this._CSPRep, msg);
       return null;
     }
 
     // scheme
     if (!this.scheme)
       newSource._scheme = that.scheme;
     else if (!that.scheme)
       newSource._scheme = this.scheme;
     if (this.scheme === "*")
       newSource._scheme = that.scheme;
     else if (that.scheme === "*")
       newSource._scheme = this.scheme;
     else if (that.scheme === this.scheme)
       newSource._scheme = this.scheme;
     else {
-      CSPError(CSPLocalizer.getFormatStr("notIntersectScheme", [this.toString(), that.toString()]));
+      var msg = CSPLocalizer.getFormatStr("notIntersectScheme",
+                                          [this.toString(), that.toString()]);
+      cspError(this._CSPRep, msg);
       return null;
     }
 
     // NOTE: Both sources must have a host, if they don't, something funny is
     // going on.  The fromString() factory method should have set the host to
     // * if there's no host specified in the input. Regardless, if a host is
     // not present either the scheme is hostless or any host should be allowed.
     // This means we can use the other source's host as the more restrictive
     // host expression, or if neither are present, we can use "*", but the
     // error should still be reported.
 
     // host
     if (this.host && that.host) {
       newSource._host = this.host.intersectWith(that.host);
     } else if (this.host) {
-      CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [that.toString()]));
+      let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
+                                          [that.toString()]);
+      cspError(this._CSPRep, msg);
       newSource._host = this.host.clone();
     } else if (that.host) {
-      CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [this.toString()]));
+      let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
+                                          [this.toString()]);
+      cspError(this._CSPRep, msg);
       newSource._host = that.host.clone();
     } else {
-      CSPError(CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts", [this.toString(), that.toString()]));
+      let msg = CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts",
+                                          [this.toString(), that.toString()]);
+      cspError(this._CSPRep, msg);
       newSource._host = CSPHost.fromString("*");
     }
 
     return newSource;
   },
 
   /**
    * Compares one CSPSource to another.
@@ -1477,17 +1558,17 @@ CSPHost.prototype = {
 function CSPViolationReportListener(reportURI) {
   this._reportURI = reportURI;
 }
 
 CSPViolationReportListener.prototype = {
   _reportURI:   null,
 
   QueryInterface: function(iid) {
-    if(iid.equals(Ci.nsIStreamListener) ||
+    if (iid.equals(Ci.nsIStreamListener) ||
         iid.equals(Ci.nsIRequestObserver) ||
         iid.equals(Ci.nsISupports))
       return this;
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
   onStopRequest:
   function(request, context, status) {
@@ -1503,16 +1584,62 @@ CSPViolationReportListener.prototype = {
 
   onDataAvailable:
   function(request, context, inputStream, offset, count) { },
 
 };
 
 //////////////////////////////////////////////////////////////////////
 
+function innerWindowFromRequest(docRequest) {
+  let win = null;
+  let loadContext = null;
+
+  try {
+    loadContext = docRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
+  } catch (ex) {
+    try {
+      loadContext = docRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
+    } catch (ex) {
+      return null;
+    }
+  }
+
+  if (loadContext) {
+    win = loadContext.associatedWindow;
+  }
+  if (win) {
+    try {
+       let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+       return winUtils.currentInnerWindowID;
+    } catch (ex) {
+      return null;
+    }
+  }
+  return null;
+}
+
+function cspError(aCSPRep, aMessage) {
+  if (aCSPRep) {
+    aCSPRep.error(aMessage);
+  } else {
+    (new CSPRep()).error(aMessage);
+  }
+}
+
+function cspWarn(aCSPRep, aMessage) {
+  if (aCSPRep) {
+    aCSPRep.warn(aMessage);
+  } else {
+    (new CSPRep()).warn(aMessage);
+  }
+}
+
+//////////////////////////////////////////////////////////////////////
+
 CSPLocalizer = {
   /**
    * Retrieve a localized string.
    *
    * @param string aName
    *        The string name you want from the CSP string bundle.
    * @return string
    *         The localized string.
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -94,16 +94,17 @@
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
+#include "mozilla/dom/NodeListBinding.h"
 #include "dombindings.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
@@ -384,17 +385,23 @@ NS_INTERFACE_TABLE_HEAD(nsChildContentLi
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsChildContentList)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NodeList)
 NS_INTERFACE_MAP_END
 
 JSObject*
 nsChildContentList::WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
+  JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::NodeList::create(cx, scope, this);
 }
 
 NS_IMETHODIMP
 nsChildContentList::GetLength(uint32_t* aLength)
 {
   *aLength = mNode ? mNode->GetChildCount() : 0;
 
   return NS_OK;
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -99,44 +99,16 @@ ContentSecurityPolicy.prototype = {
   get allowsInlineScript() {
     return this._reportOnlyMode || this._policy.allowsInlineScripts;
   },
 
   get allowsEval() {
     return this._reportOnlyMode || this._policy.allowsEvalInScripts;
   },
 
-  get innerWindowID() {
-    let win = null;
-    let loadContext = null;
-
-    try {
-      loadContext = this._docRequest
-                        .notificationCallbacks.getInterface(Ci.nsILoadContext);
-    } catch (ex) {
-      try {
-        loadContext = this._docRequest.loadGroup
-                          .notificationCallbacks.getInterface(Ci.nsILoadContext);
-      } catch (ex) {
-      }
-    }
-
-    if (loadContext) {
-      win = loadContext.associatedWindow;
-    }
-    if (win) {
-      try {
-         let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-         return winUtils.currentInnerWindowID;
-      } catch (ex) {
-      }
-    }
-    return null;
-  },
-
   /**
    * Log policy violation on the Error Console and send a report if a report-uri
    * is present in the policy
    *
    * @param aViolationType
    *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    * @param aSourceFile
    *     name of the source file containing the violation (if available)
@@ -297,53 +269,52 @@ ContentSecurityPolicy.prototype = {
         report["csp-report"]["script-sample"] = aScriptSample;
       if (aLineNum)
         report["csp-report"]["line-number"] = aLineNum;
 
       var reportString = JSON.stringify(report);
       CSPdebug("Constructed violation report:\n" + reportString);
 
       var violationMessage = null;
-      if(blockedUri["asciiSpec"]){
+      if (blockedUri["asciiSpec"]) {
          violationMessage = CSPLocalizer.getFormatStr("directiveViolatedWithURI", [violatedDirective, blockedUri.asciiSpec]);
       } else {
          violationMessage = CSPLocalizer.getFormatStr("directiveViolated", [violatedDirective]);
       }
-      CSPWarning(violationMessage,
-                 this.innerWindowID,
-                 (aSourceFile) ? aSourceFile : null,
-                 (aScriptSample) ? decodeURIComponent(aScriptSample) : null,
-                 (aLineNum) ? aLineNum : null);
+      this._policy.warn(violationMessage,
+                        (aSourceFile) ? aSourceFile : null,
+                        (aScriptSample) ? decodeURIComponent(aScriptSample) : null,
+                        (aLineNum) ? aLineNum : null);
 
       // For each URI in the report list, send out a report.
       // We make the assumption that all of the URIs are absolute URIs; this
       // should be taken care of in CSPRep.fromString (where it converts any
       // relative URIs into absolute ones based on "self").
       for (let i in uris) {
         if (uris[i] === "")
           continue;
 
         try {
           var chan = Services.io.newChannel(uris[i], null, null);
-          if(!chan) {
+          if (!chan) {
             CSPdebug("Error creating channel for " + uris[i]);
             continue;
           }
 
           var content = Cc["@mozilla.org/io/string-input-stream;1"]
                           .createInstance(Ci.nsIStringInputStream);
           content.data = reportString + "\n\n";
 
           // make sure this is an anonymous request (no cookies) so in case the
           // policy URI is injected, it can't be abused for CSRF.
           chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
 
           // we need to set an nsIChannelEventSink on the channel object
           // so we can tell it to not follow redirects when posting the reports
-          chan.notificationCallbacks = new CSPReportRedirectSink();
+          chan.notificationCallbacks = new CSPReportRedirectSink(this._policy);
 
           chan.QueryInterface(Ci.nsIUploadChannel)
               .setUploadStream(content, "application/json", content.available());
 
           try {
             // if this is an HTTP channel, set the request method to post
             chan.QueryInterface(Ci.nsIHttpChannel);
             chan.requestMethod = "POST";
@@ -364,18 +335,18 @@ ContentSecurityPolicy.prototype = {
           }
 
           //send data (and set up error notifications)
           chan.asyncOpen(new CSPViolationReportListener(uris[i]), null);
           CSPdebug("Sent violation report to " + uris[i]);
         } catch(e) {
           // it's possible that the URI was invalid, just log a
           // warning and skip over that.
-          CSPWarning(CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]), this.innerWindowID);
-          CSPWarning(CSPLocalizer.getFormatStr("errorWas", [e.toString()]), this.innerWindowID);
+          this._policy.warn(CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
+          this._policy.warn(CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
         }
       }
     }
   },
 
   /**
    * Exposed Method to analyze docShell for approved frame ancestry.
    * Also sends violation reports if necessary.
@@ -547,17 +518,18 @@ ContentSecurityPolicy.prototype = {
                                  aSourceFile, aScriptSample, aLineNum);
       }, Ci.nsIThread.DISPATCH_NORMAL);
   },
 };
 
 // The POST of the violation report (if it happens) should not follow
 // redirects, per the spec. hence, we implement an nsIChannelEventSink
 // with an object so we can tell XHR to abort if a redirect happens.
-function CSPReportRedirectSink() {
+function CSPReportRedirectSink(policy) {
+  this._policy = policy;
 }
 
 CSPReportRedirectSink.prototype = {
   QueryInterface: function requestor_qi(iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsIInterfaceRequestor) ||
         iid.equals(Ci.nsIChannelEventSink))
       return this;
@@ -570,17 +542,17 @@ CSPReportRedirectSink.prototype = {
       return this;
 
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
   // nsIChannelEventSink
   asyncOnChannelRedirect: function channel_redirect(oldChannel, newChannel,
                                                     flags, callback) {
-    CSPWarning(CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec]));
+    this._policy.warn(CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec]));
 
     // cancel the old channel so XHR failure callback happens
     oldChannel.cancel(Cr.NS_ERROR_ABORT);
 
     // notify an observer that we have blocked the report POST due to a redirect,
     // used in testing, do this async since we're in an async call now to begin with
     Services.tm.mainThread.dispatch(
       function() {
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -14,19 +14,19 @@
 #include "nsIContent.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsGenericElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsContentUtils.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsGkAtoms.h"
-
+#include "mozilla/dom/HTMLCollectionBinding.h"
+#include "mozilla/dom/NodeListBinding.h"
 #include "dombindings.h"
-#include "mozilla/dom/BindingUtils.h"
 
 // Form related includes
 #include "nsIDOMHTMLFormElement.h"
 
 #include "pldhash.h"
 
 #ifdef DEBUG_CONTENT_LIST
 #include "nsIContentIterator.h"
@@ -158,17 +158,23 @@ NS_INTERFACE_MAP_END_INHERITING(nsBaseCo
 
 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
 
 JSObject*
 nsSimpleContentList::WrapObject(JSContext *cx, JSObject *scope,
                                 bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
+  JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::NodeList::create(cx, scope, this);
 }
 
 // nsFormContentList
 
 nsFormContentList::nsFormContentList(nsIContent *aForm,
                                      nsBaseContentList& aContentList)
   : nsSimpleContentList(aForm)
 {
@@ -292,25 +298,37 @@ const nsCacheableFuncStringContentList::
 const nsCacheableFuncStringContentList::ContentListType
   nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
 #endif
 
 JSObject*
 nsCacheableFuncStringNodeList::WrapObject(JSContext *cx, JSObject *scope,
                                           bool *triedToWrap)
 {
-  return oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
+  JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::NodeList::create(cx, scope, this);
 }
 
 
 JSObject*
 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JSObject *scope,
                                                 bool *triedToWrap)
 {
-  return oldproxybindings::HTMLCollection::create(cx, scope, this, triedToWrap);
+  JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::HTMLCollection::create(cx, scope, this);
 }
 
 // Hashtable for storing nsCacheableFuncStringContentList
 static PLDHashTable gFuncStringContentListHashTable;
 
 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
 {
   nsCacheableFuncStringContentList* mContentList;
@@ -529,18 +547,23 @@ nsContentList::~nsContentList()
     // Clean up mData
     (*mDestroyFunc)(mData);
   }
 }
 
 JSObject*
 nsContentList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
-                                                       triedToWrap);
+  JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::HTMLCollection::create(cx, scope, this);
 }
 
 DOMCI_DATA(ContentList, nsContentList)
 
 // QueryInterface implementation for nsContentList
 NS_INTERFACE_TABLE_HEAD(nsContentList)
   NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsContentList)
     NS_CONTENT_LIST_INTERFACES(nsContentList)
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -30,16 +30,17 @@
 #include "nsStringStream.h"
 #include "nsJSUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Attributes.h"
 
 #include "plbase64.h"
 #include "prmem.h"
+#include "mozilla/dom/FileListBinding.h"
 #include "dombindings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // XXXkhuey the input stream that we pass out of a DOMFile
 // can outlive the actual DOMFile object.  Thus, we must
 // ensure that the buffer underlying the stream we get
@@ -675,17 +676,23 @@ NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileList)
 
 JSObject*
 nsDOMFileList::WrapObject(JSContext *cx, JSObject *scope,
                           bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::FileList::create(cx, scope, this, triedToWrap);
+  JSObject* obj = FileListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::FileList::create(cx, scope, this);
 }
 
 nsIDOMFile*
 nsDOMFileList::GetItemAt(uint32_t aIndex)
 {
   return Item(aIndex);
 }
 
--- a/content/base/src/nsDOMSettableTokenList.cpp
+++ b/content/base/src/nsDOMSettableTokenList.cpp
@@ -2,16 +2,17 @@
  * 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/. */
 
 /*
  * Implementation of nsIDOMDOMSettableTokenList specified by HTML5.
  */
 
 #include "nsDOMSettableTokenList.h"
+#include "mozilla/dom/DOMSettableTokenListBinding.h"
 #include "dombindings.h"
 
 
 nsDOMSettableTokenList::nsDOMSettableTokenList(nsGenericElement *aElement, nsIAtom* aAttrAtom)
   : nsDOMTokenList(aElement, aAttrAtom)
 {
 }
 
@@ -46,11 +47,19 @@ nsDOMSettableTokenList::SetValue(const n
 
   return mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
 }
 
 JSObject*
 nsDOMSettableTokenList::WrapObject(JSContext *cx, JSObject *scope,
                                    bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::DOMSettableTokenList::create(cx, scope, this,
-                                                             triedToWrap);
+  JSObject* obj = mozilla::dom::DOMSettableTokenListBinding::Wrap(cx, scope,
+                                                                  this,
+                                                                  triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::DOMSettableTokenList::create(cx, scope,
+                                                                      this);
 }
--- a/content/base/src/nsDOMTokenList.cpp
+++ b/content/base/src/nsDOMTokenList.cpp
@@ -7,16 +7,17 @@
  */
 
 #include "nsDOMTokenList.h"
 
 #include "nsAttrValue.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsGenericElement.h"
+#include "mozilla/dom/DOMTokenListBinding.h"
 #include "dombindings.h"
 #include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsDOMTokenList::nsDOMTokenList(nsGenericElement* aElement, nsIAtom* aAttrAtom)
   : mElement(aElement),
@@ -307,12 +308,17 @@ nsDOMTokenList::ToString(nsAString& aRes
 {
   Stringify(aResult);
   return NS_OK;
 }
 
 JSObject*
 nsDOMTokenList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::DOMTokenList::create(cx, scope, this,
-                                                     triedToWrap);
+  JSObject* obj = DOMTokenListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::DOMTokenList::create(cx, scope, this);
 }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2453,17 +2453,17 @@ nsDocument::InitCSP(nsIChannel* aChannel
         tCspROHeaderValue);
   }
   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
 
   // ----- Figure out if we need to apply an app default CSP
   bool applyAppDefaultCSP = false;
   nsIPrincipal* principal = NodePrincipal();
-  PRUint16 appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   bool unknownAppId;
   if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
       !unknownAppId &&
       NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
     applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
                            appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
   }
 #ifdef PR_LOGGING
@@ -8419,17 +8419,17 @@ NotifyAudioAvailableListener(nsIContent 
 void
 nsDocument::NotifyAudioAvailableListener()
 {
   mHasAudioAvailableListener = true;
   EnumerateFreezableElements(::NotifyAudioAvailableListener, nullptr);
 }
 
 nsresult
-nsDocument::RemoveImage(imgIRequest* aImage)
+nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
 {
   NS_ENSURE_ARG_POINTER(aImage);
 
   // Get the old count. It should exist and be > 0.
   uint32_t count = 0;
   DebugOnly<bool> found = mImageTracker.Get(aImage, &count);
   NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!");
   NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!");
@@ -8455,20 +8455,22 @@ nsDocument::RemoveImage(imgIRequest* aIm
   }
 
   // If we're animating images, remove our request to animate this one.
   if (mAnimatingImages) {
     nsresult rv2 = aImage->DecrementAnimationConsumers();
     rv = NS_SUCCEEDED(rv) ? rv2 : rv;
   }
 
-  // Request that the image be discarded if nobody else holds a lock on it.
-  // Do this even if !mLockingImages, because even if we didn't just unlock
-  // this image, it might still be a candidate for discarding.
-  aImage->RequestDiscard();
+  if (aFlags & REQUEST_DISCARD) {
+    // Request that the image be discarded if nobody else holds a lock on it.
+    // Do this even if !mLockingImages, because even if we didn't just unlock
+    // this image, it might still be a candidate for discarding.
+    aImage->RequestDiscard();
+  }
 
   return rv;
 }
 
 nsresult
 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
 {
   MOZ_ASSERT(aPlugin);
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -910,17 +910,17 @@ public:
                            const nsAString& aLocalName);
 
   virtual Element *GetElementById(const nsAString& aElementId);
   virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const;
 
   virtual Element *LookupImageElement(const nsAString& aElementId);
 
   virtual NS_HIDDEN_(nsresult) AddImage(imgIRequest* aImage);
-  virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
+  virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage, uint32_t aFlags);
   virtual NS_HIDDEN_(nsresult) SetImageLockingState(bool aLocked);
 
   // AddPlugin adds a plugin-related element to mPlugins when the element is
   // added to the tree.
   virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin);
   // RemovePlugin removes a plugin-related element to mPlugins when the
   // element is removed from the tree.
   virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin);
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -97,17 +97,16 @@
 #include "nsIViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
-#include "dombindings.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -1178,19 +1178,19 @@ nsImageLoadingContent::UnbindFromTree(bo
     return;
 
   // Push a null JSContext on the stack so that callbacks triggered by the
   // below code won't think they're being called from JS.
   nsCxPusher pusher;
   pusher.PushNull();
 
   if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED)
-    doc->RemoveImage(mCurrentRequest);
+    doc->RemoveImage(mCurrentRequest, nsIDocument::REQUEST_DISCARD);
   if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED)
-    doc->RemoveImage(mPendingRequest);
+    doc->RemoveImage(mPendingRequest, nsIDocument::REQUEST_DISCARD);
 }
 
 nsresult
 nsImageLoadingContent::TrackImage(imgIRequest* aImage)
 {
   if (!aImage)
     return NS_OK;
 
@@ -1222,17 +1222,17 @@ nsImageLoadingContent::UntrackImage(imgI
     mPendingRequestFlags &= ~REQUEST_SHOULD_BE_TRACKED;
   }
 
   // If GetOurDocument() returns null here, we've outlived our document.
   // That's fine, because the document empties out the tracker and unlocks
   // all locked images on destruction.
   nsIDocument* doc = GetOurCurrentDoc();
   if (doc)
-    return doc->RemoveImage(aImage);
+    return doc->RemoveImage(aImage, nsIDocument::REQUEST_DISCARD);
   return NS_OK;
 }
 
 
 void
 nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const
 {
   aDest->mCurrentRequest = nsContentUtils::GetStaticRequest(mCurrentRequest);
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -223,25 +223,17 @@ nsInProcessTabChildGlobal::Disconnect()
 
 void
 nsInProcessTabChildGlobal::DelayedDisconnect()
 {
   // Don't let the event escape
   mOwner = nullptr;
 
   // Fire the "unload" event
-  nsCOMPtr<nsIDOMEvent> event;
-  NS_NewDOMEvent(getter_AddRefs(event), nullptr, nullptr);
-  if (event) {
-    event->InitEvent(NS_LITERAL_STRING("unload"), false, false);
-    event->SetTrusted(true);
-
-    bool dummy;
-    nsDOMEventTargetHelper::DispatchEvent(event, &dummy);
-  }
+  nsDOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
 
   // Continue with the Disconnect cleanup
   nsCOMPtr<nsIDOMWindow> win = do_GetInterface(mDocShell);
   nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(win);
   if (pwin) {
     pwin->SetChromeEventHandler(pwin->GetChromeEventHandler());
   }
   mDocShell = nullptr;
--- a/content/base/src/nsMixedContentBlocker.cpp
+++ b/content/base/src/nsMixedContentBlocker.cpp
@@ -71,24 +71,24 @@ nsMixedContentBlocker::nsMixedContentBlo
 
 nsMixedContentBlocker::~nsMixedContentBlocker()
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsMixedContentBlocker, nsIContentPolicy)
 
 NS_IMETHODIMP
-nsMixedContentBlocker::ShouldLoad(PRUint32 aContentType,
+nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
                                   nsIURI* aContentLocation,
                                   nsIURI* aRequestingLocation,
                                   nsISupports* aRequestingContext,
                                   const nsACString& aMimeGuess,
                                   nsISupports* aExtra,
                                   nsIPrincipal* aRequestPrincipal,
-                                  PRInt16* aDecision)
+                                  int16_t* aDecision)
 {
   // Default policy: allow the load if we find no reason to block it.
   *aDecision = nsIContentPolicy::ACCEPT;
 
   // If mixed script blocking and mixed display blocking are turned off
   // we can return early
   if (!sBlockMixedScript && !sBlockMixedDisplay) {
     return NS_OK;
@@ -181,24 +181,24 @@ nsMixedContentBlocker::ShouldLoad(PRUint
       // other types of mixed content are allowed
       break;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsMixedContentBlocker::ShouldProcess(PRUint32 aContentType,
+nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
                                      nsIURI* aContentLocation,
                                      nsIURI* aRequestingLocation,
                                      nsISupports* aRequestingContext,
                                      const nsACString& aMimeGuess,
                                      nsISupports* aExtra,
                                      nsIPrincipal* aRequestPrincipal,
-                                     PRInt16* aDecision)
+                                     int16_t* aDecision)
 {
   if(!aContentLocation) {
     // aContentLocation may be null when a plugin is loading without an associated URI resource
     if(aContentType == TYPE_OBJECT) {
        return NS_OK;
     } else {
        return NS_ERROR_FAILURE;
     }
--- a/content/base/test/unit/test_csputils.js
+++ b/content/base/test/unit/test_csputils.js
@@ -154,78 +154,78 @@ test(
     });
 
 ///////////////////// Test the Source object //////////////////////
 
 test(
     function test_CSPSource_fromString() {
     // can't do these tests because "self" is not defined.
       //"basic source should not be null.");
-      do_check_neq(null, CSPSource.fromString("a.com", "http://abc.com"));
+      do_check_neq(null, CSPSource.fromString("a.com", undefined, "http://abc.com"));
 
       //"ldh characters should all work for host.");
-      do_check_neq(null, CSPSource.fromString("a2-c.com", "https://a.com"));
+      do_check_neq(null, CSPSource.fromString("a2-c.com", undefined, "https://a.com"));
 
       //"wildcard should work in first token for host.");
-      do_check_neq(null, CSPSource.fromString("*.a.com", "http://abc.com"));
+      do_check_neq(null, CSPSource.fromString("*.a.com", undefined, "http://abc.com"));
 
       //print(" --- Ignore the following two errors if they print ---");
       //"wildcard should not work in non-first token for host.");
-      do_check_eq(null, CSPSource.fromString("x.*.a.com", "http://a.com"));
+      do_check_eq(null, CSPSource.fromString("x.*.a.com", undefined, "http://a.com"));
 
       //"funny characters (#) should not work for host.");
-      do_check_eq(null, CSPSource.fromString("a#2-c.com", "http://a.com"));
+      do_check_eq(null, CSPSource.fromString("a#2-c.com", undefined, "http://a.com"));
 
       //print(" --- Stop ignoring errors that print ---\n");
 
       //"failed to parse host with port.");
-      do_check_neq(null, CSPSource.create("a.com:23", "http://a.com"));
+      do_check_neq(null, CSPSource.create("a.com:23", undefined, "http://a.com"));
       //"failed to parse host with scheme.");
-      do_check_neq(null, CSPSource.create("https://a.com", "http://a.com"));
+      do_check_neq(null, CSPSource.create("https://a.com", undefined, "http://a.com"));
       //"failed to parse host with scheme and port.");
-      do_check_neq(null, CSPSource.create("https://a.com:200", "http://a.com"));
+      do_check_neq(null, CSPSource.create("https://a.com:200", undefined, "http://a.com"));
 
       //Check to make sure we don't match multiple instances with regex
       do_check_eq(null, CSPSource.create("http://foo.com:bar.com:23"));
       //Port parsing should work for all schemes
       do_check_neq(null, CSPSource.create("data:"));
       do_check_neq(null, CSPSource.create("javascript:"));
     });
 
 test(
     function test_CSPSource_fromString_withSelf() {
       var src;
-      src = CSPSource.create("a.com", "https://foobar.com:443");
+      src = CSPSource.create("a.com", undefined, "https://foobar.com:443");
       //"src should inherit port *
       do_check_true(src.permits("https://a.com:443"));
       //"src should inherit and require https scheme
       do_check_false(src.permits("http://a.com"));
       //"src should inherit scheme 'https'"
       do_check_true(src.permits("https://a.com"));
 
-      src = CSPSource.create("http://a.com", "https://foobar.com:443");
+      src = CSPSource.create("http://a.com", undefined, "https://foobar.com:443");
       //"src should inherit and require http scheme"
       do_check_false(src.permits("https://a.com"));
       //"src should inherit scheme 'http'"
       do_check_true(src.permits("http://a.com"));
       //"src should inherit port and scheme from parent"
       //"src should inherit default port for 'http'"
       do_check_true(src.permits("http://a.com:80"));
 
-      src = CSPSource.create("'self'", "https://foobar.com:443");
+      src = CSPSource.create("'self'", undefined, "https://foobar.com:443");
       //"src should inherit port *
       do_check_true(src.permits("https://foobar.com:443"));
       //"src should inherit and require https scheme
       do_check_false(src.permits("http://foobar.com"));
       //"src should inherit scheme 'https'"
       do_check_true(src.permits("https://foobar.com"));
       //"src should reject other hosts"
       do_check_false(src.permits("https://a.com"));
 
-      src = CSPSource.create("javascript:", "https://foobar.com:443");
+      src = CSPSource.create("javascript:", undefined, "https://foobar.com:443");
       //"hostless schemes should be parseable."
       var aUri = NetUtil.newURI("javascript:alert('foo');");
       do_check_true(src.permits(aUri));
       //"src should reject other hosts"
       do_check_false(src.permits("https://a.com"));
       //"nothing else should be allowed"
       do_check_false(src.permits("https://foobar.com"));
 
@@ -255,30 +255,31 @@ test(
       do_check_true(CSPSourceList.fromString("https://f-oo.bar:3f").isNone());
       //print(" --- Stop ignoring errors that print ---\n");
     });
 
 test(
     function test_CSPSourceList_fromString_twohost() {
       var str = "foo.bar:21 https://ras.bar";
       var parsed = "http://foo.bar:21 https://ras.bar:443";
-      var sd = CSPSourceList.fromString(str, URI("http://self.com:80"));
+      var sd = CSPSourceList.fromString(str, undefined, URI("http://self.com:80"));
       //"two-host list should parse"
       do_check_neq(null,sd);
       //"two-host list should parse to two hosts"
       do_check_eq(2, sd._sources.length);
       //"two-host list should contain original data"
       do_check_eq(parsed, sd.toString());
     });
 
 test(
     function test_CSPSourceList_permits() {
       var nullSourceList = CSPSourceList.fromString("'none'");
-      var simpleSourceList = CSPSourceList.fromString("a.com", URI("http://self.com"));
+      var simpleSourceList = CSPSourceList.fromString("a.com", undefined, URI("http://self.com"));
       var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88",
+                                                      undefined,
                                                       URI("http://self.com:88"));
       var allSourceList = CSPSourceList.fromString("*");
       var allAndMoreSourceList = CSPSourceList.fromString("* https://bar.com 'none'");
 
       //'none' should permit none."
       do_check_false( nullSourceList.permits("http://a.com"));
       //a.com should permit a.com"
       do_check_true( simpleSourceList.permits("http://a.com"));
@@ -675,33 +676,33 @@ test(
        *       "allow 'self' a.com"
        *   Explicit policy:
        *       "allow https://foobar.com:4443 https://a.com:443"
        *
        * This test examines scheme and nonstandard port inheritance.
        */
 
       var src;
-      src = CSPSource.create("a.com", "https://foobar.com:4443");
+      src = CSPSource.create("a.com", undefined, "https://foobar.com:4443");
       //"src should inherit and require https scheme
       do_check_false(src.permits("http://a.com"));
       //"src should inherit scheme 'https'"
       do_check_true(src.permits("https://a.com"));
       //"src should get default port
       do_check_true(src.permits("https://a.com:443"));
 
-      src = CSPSource.create("http://a.com", "https://foobar.com:4443");
+      src = CSPSource.create("http://a.com", undefined, "https://foobar.com:4443");
       //"src should require http scheme"
       do_check_false(src.permits("https://a.com"));
       //"src should keep scheme 'http'"
       do_check_true(src.permits("http://a.com"));
       //"src should inherit default port for 'http'"
       do_check_true(src.permits("http://a.com:80"));
 
-      src = CSPSource.create("'self'", "https://foobar.com:4443");
+      src = CSPSource.create("'self'", undefined, "https://foobar.com:4443");
       //"src should inherit nonstandard port from self
       do_check_true(src.permits("https://foobar.com:4443"));
       do_check_false(src.permits("https://foobar.com"));
       do_check_false(src.permits("https://foobar.com:443"));
 
       //"src should inherit and require https scheme from self
       do_check_false(src.permits("http://foobar.com:4443"));
       do_check_false(src.permits("http://foobar.com"));
@@ -711,19 +712,19 @@ test(
 test(
     function test_bug634773_noneAndStarAreDifferent() {
       /**
        * Bug 634773 is that allow * and allow 'none' end up "equal" via
        * CSPSourceList.prototype.equals(), which is wrong.  This tests that
        * doesn't happen.
        */
 
-      var p_none = CSPSourceList.fromString("'none'", "http://foo.com", false);
-      var p_all = CSPSourceList.fromString("*", "http://foo.com", false);
-      var p_one = CSPSourceList.fromString("bar.com", "http://foo.com", false);
+      var p_none = CSPSourceList.fromString("'none'", undefined, "http://foo.com", false);
+      var p_all = CSPSourceList.fromString("*", undefined, "http://foo.com", false);
+      var p_one = CSPSourceList.fromString("bar.com", undefined, "http://foo.com", false);
 
       do_check_false(p_none.equals(p_all));
       do_check_false(p_none.equals(p_one));
       do_check_false(p_all.equals(p_none));
       do_check_false(p_all.equals(p_one));
 
       do_check_true(p_all.permits("http://bar.com"));
       do_check_true(p_one.permits("http://bar.com"));
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -7,16 +7,17 @@
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsGUIEvent.h"
 #include "nsIDocument.h"
 #include "nsIJSContextStack.h"
 #include "nsDOMJSUtils.h"
 #include "prprf.h"
 #include "nsGlobalWindow.h"
+#include "nsDOMEvent.h"
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDOMEventTargetHelper)
@@ -200,16 +201,36 @@ nsDOMEventTargetHelper::DispatchEvent(ns
   nsresult rv =
     nsEventDispatcher::DispatchDOMEvent(this, nullptr, aEvent, nullptr, &status);
 
   *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
   return rv;
 }
 
 nsresult
+nsDOMEventTargetHelper::DispatchTrustedEvent(const nsAString& aEventName)
+{
+  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
+  nsresult rv = event->InitEvent(aEventName, false, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return DispatchTrustedEvent(event);
+}
+
+nsresult
+nsDOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent* event)
+{
+  nsresult rv = event->SetTrusted(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool dummy;
+  return DispatchEvent(event, &dummy);
+}
+
+nsresult
 nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
                                         JSContext* aCx,
                                         const JS::Value& aValue)
 {
   nsEventListenerManager* elm = GetListenerManager(true);
 
   JSObject* obj = GetWrapper();
   if (!obj) {
--- a/content/events/src/nsDOMEventTargetHelper.h
+++ b/content/events/src/nsDOMEventTargetHelper.h
@@ -14,16 +14,18 @@
 #include "nsPIDOMWindow.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsEventListenerManager.h"
 #include "nsIScriptContext.h"
 #include "nsWrapperCache.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Attributes.h"
 
+class nsDOMEvent;
+
 class nsDOMEventTargetHelper : public nsIDOMEventTarget,
                                public nsWrapperCache
 {
 public:
   nsDOMEventTargetHelper() : mOwner(nullptr), mHasOrHasHadOwner(false) {}
   virtual ~nsDOMEventTargetHelper();
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsDOMEventTargetHelper)
@@ -109,16 +111,20 @@ public:
 
   void BindToOwner(nsPIDOMWindow* aOwner);
   void BindToOwner(nsDOMEventTargetHelper* aOther);
   virtual void DisconnectFromOwner();                   
   nsPIDOMWindow* GetOwner() const { return mOwner; }
   bool HasOrHasHadOwner() { return mHasOrHasHadOwner; }
 protected:
   nsRefPtr<nsEventListenerManager> mListenerManager;
+  // Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
+  nsresult DispatchTrustedEvent(const nsAString& aEventName);
+  // Make |event| trusted and dispatch |aEvent| to |this|.
+  nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);
 private:
   // These may be null (native callers or xpcshell).
   nsPIDOMWindow*             mOwner; // Inner window.
   bool                       mHasOrHasHadOwner;
 };
 
 #define NS_IMPL_EVENT_HANDLER(_class, _event)                                 \
     NS_IMETHODIMP _class::GetOn##_event(JSContext* aCx, JS::Value* aValue)    \
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -168,23 +168,18 @@ nsEventListenerManager::GetInnerWindowFo
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
   if (node) {
     // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
     // if that's the XBL document?
     return node->OwnerDoc()->GetInnerWindow();
   }
 
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
-  if (window) {
-    NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
-    return window;
-  }
-
-  return nullptr;
+  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
+  return window;
 }
 
 already_AddRefed<nsPIDOMWindow>
 nsEventListenerManager::GetTargetAsInnerWindow() const
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
   if (!window) {
     return nullptr;
@@ -285,17 +280,20 @@ nsEventListenerManager::AddEventListener
     EnableDevice(NS_DEVICE_ORIENTATION);
   } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
     EnableDevice(NS_DEVICE_PROXIMITY);
   } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
     EnableDevice(NS_DEVICE_LIGHT);
   } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
     EnableDevice(NS_DEVICE_MOTION);
   } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
-    EnableTimeChangeNotifications();
+    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
+    if (window) {
+      window->EnableTimeChangeNotifications();
+    }
   } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
       window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
     }
   } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
@@ -343,23 +341,21 @@ nsEventListenerManager::IsDeviceType(uin
       break;
   }
   return false;
 }
 
 void
 nsEventListenerManager::EnableDevice(uint32_t aType)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
+  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
-  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
-
   switch (aType) {
     case NS_DEVICE_ORIENTATION:
       window->EnableDeviceSensor(SENSOR_ORIENTATION);
       break;
     case NS_DEVICE_PROXIMITY:
     case NS_USER_PROXIMITY:
       window->EnableDeviceSensor(SENSOR_PROXIMITY);
       break;
@@ -375,23 +371,21 @@ nsEventListenerManager::EnableDevice(uin
       NS_WARNING("Enabling an unknown device sensor.");
       break;
   }
 }
 
 void
 nsEventListenerManager::DisableDevice(uint32_t aType)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
+  nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
-  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
-
   switch (aType) {
     case NS_DEVICE_ORIENTATION:
       window->DisableDeviceSensor(SENSOR_ORIENTATION);
       break;
     case NS_DEVICE_MOTION:
       window->DisableDeviceSensor(SENSOR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_GYROSCOPE);
@@ -447,17 +441,20 @@ nsEventListenerManager::RemoveEventListe
         --typeCount;
       }
     }
   }
 
   if (deviceType && typeCount == 0) {
     DisableDevice(aType);
   } else if (timeChangeEvent && typeCount == 0) {
-    DisableTimeChangeNotifications();
+    nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
+    if (window) {
+      window->DisableTimeChangeNotifications();
+    }
   } else if (networkEvent && typeCount == 0) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
       window->DisableNetworkEvent(aType);
     }
   }
 }
 
@@ -619,21 +616,18 @@ nsEventListenerManager::SetEventHandler(
 
   if (node) {
     // Try to get context from doc
     // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
     // if that's the XBL document?
     doc = node->OwnerDoc();
     global = doc->GetScriptGlobalObject();
   } else {
-    nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mTarget));
+    nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow();
     if (win) {
-      NS_ASSERTION(win->IsInnerWindow(),
-                   "Event listener added to outer window!");
-
       nsCOMPtr<nsIDOMDocument> domdoc;
       win->GetDocument(getter_AddRefs(domdoc));
       doc = do_QueryInterface(domdoc);
       global = do_QueryInterface(win);
     } else {
       global = do_QueryInterface(mTarget);
     }
   }
@@ -1194,32 +1188,8 @@ nsEventListenerManager::UnmarkGrayJSList
     if (jsl) {
       xpc_UnmarkGrayObject(jsl->GetHandler());
       xpc_UnmarkGrayObject(jsl->GetEventScope());
     } else if (ls.mListenerType == eWrappedJSListener) {
       xpc_TryUnmarkWrappedGrayObject(ls.mListener);
     }
   }
 }
-
-void
-nsEventListenerManager::EnableTimeChangeNotifications()
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
-  if (!window) {
-    return;
-  }
-
-  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
-  window->EnableTimeChangeNotifications();
-}
-
-void
-nsEventListenerManager::DisableTimeChangeNotifications()
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
-  if (!window) {
-    return;
-  }
-
-  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
-  window->DisableTimeChangeNotifications();
-}
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -35,18 +35,18 @@ typedef enum
     eNativeListener = 0,
     eJSEventListener,
     eWrappedJSListener
 } nsListenerType;
 
 struct nsListenerStruct
 {
   nsRefPtr<nsIDOMEventListener> mListener;
+  nsCOMPtr<nsIAtom>             mTypeAtom;
   uint32_t                      mEventType;
-  nsCOMPtr<nsIAtom>             mTypeAtom;
   uint16_t                      mFlags;
   uint8_t                       mListenerType;
   bool                          mListenerIsHandler : 1;
   bool                          mHandlerIsString : 1;
 
   nsIJSEventListener* GetJSListener() const {
     return (mListenerType == eJSEventListener) ?
       static_cast<nsIJSEventListener *>(mListener.get()) : nullptr;
@@ -264,19 +264,16 @@ protected:
                                    JSObject *aHandler,
                                    bool aPermitUntrustedEvents,
                                    nsListenerStruct **aListenerStruct);
 
   bool IsDeviceType(uint32_t aType);
   void EnableDevice(uint32_t aType);
   void DisableDevice(uint32_t aType);
 
-  void EnableTimeChangeNotifications();
-  void DisableTimeChangeNotifications();
-
 public:
   /**
    * Set the "inline" event listener for aEventName to |v|.  This
    * might actually remove the event listener, depending on the value
    * of |v|.  Note that on entry to this function cx and aScope might
    * not be in the same compartment, though cx and v are guaranteed to
    * be in the same compartment.  If aExpectScriptContext is false,
    * not finding an nsIScriptContext does not cause failure.
--- a/content/events/src/nsPaintRequest.cpp
+++ b/content/events/src/nsPaintRequest.cpp
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsPaintRequest.h"
 
 #include "nsDOMClassInfoID.h"
 #include "nsClientRect.h"
 #include "nsIFrame.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/PaintRequestListBinding.h"
+#include "dombindings.h"
 
 DOMCI_DATA(PaintRequest, nsPaintRequest)
 
 NS_INTERFACE_TABLE_HEAD(nsPaintRequest)
   NS_INTERFACE_TABLE1(nsPaintRequest, nsIDOMPaintRequest)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(PaintRequest)
 NS_INTERFACE_MAP_END
@@ -48,16 +50,31 @@ NS_INTERFACE_TABLE_HEAD(nsPaintRequestLi
   NS_INTERFACE_TABLE1(nsPaintRequestList, nsIDOMPaintRequestList)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsPaintRequestList)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(PaintRequestList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPaintRequestList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPaintRequestList)
 
+JSObject*
+nsPaintRequestList::WrapObject(JSContext *cx, JSObject *scope,
+                               bool *triedToWrap)
+{
+  JSObject* obj = mozilla::dom::PaintRequestListBinding::Wrap(cx, scope, this,
+                                                              triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::PaintRequestList::create(cx, scope,
+                                                                  this);
+}
+
 NS_IMETHODIMP    
 nsPaintRequestList::GetLength(uint32_t* aLength)
 {
   *aLength = Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP    
--- a/content/events/src/nsPaintRequest.h
+++ b/content/events/src/nsPaintRequest.h
@@ -5,18 +5,18 @@
 
 #ifndef NSPAINTREQUEST_H_
 #define NSPAINTREQUEST_H_
 
 #include "nsIDOMPaintRequest.h"
 #include "nsIDOMPaintRequestList.h"
 #include "nsPresContext.h"
 #include "nsIDOMEvent.h"
-#include "dombindings.h"
 #include "mozilla/Attributes.h"
+#include "nsWrapperCache.h"
 
 class nsPaintRequest MOZ_FINAL : public nsIDOMPaintRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMPAINTREQUEST
 
   nsPaintRequest() { mRequest.mFlags = 0; }
@@ -39,22 +39,17 @@ public:
     SetIsDOMBinding();
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsPaintRequestList)
   NS_DECL_NSIDOMPAINTREQUESTLIST
   
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
-                               bool *triedToWrap)
-  {
-    return mozilla::dom::oldproxybindings::PaintRequestList::create(cx, scope, this,
-                                                           triedToWrap);
-  }
-
+                               bool *triedToWrap);
   nsISupports* GetParentObject()
   {
     return mParent;
   }
 
   void Append(nsIDOMPaintRequest* aElement) { mArray.AppendObject(aElement); }
 
   static nsPaintRequestList* FromSupports(nsISupports* aSupports)
--- a/content/events/test/test_bug574663.html
+++ b/content/events/test/test_bug574663.html
@@ -46,29 +46,36 @@ function runTest() {
     var scrollbox = win.document.getElementById("scrollbox");
     let winUtils = SpecialPowers.getDOMWindowUtils(win);
     let outstandingTests = [
       [false, false],
       [false, true],
       [true, false],
       [true, true],
     ];
+
+    // grab the refresh driver, since we want to make sure
+    // async scrolls happen in deterministic time
+    winUtils.advanceTimeAndRefresh(1000);
+
     function nextTest() {
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       if (!outstandingTests.length) {
         win.close();
         clearPrefs();
         SimpleTest.finish();
         return;
       }
 
       let [ctrlKey, isMomentum] = outstandingTests.shift();
       let scrollTopBefore = scrollbox.scrollTop;
       let zoomFactorBefore = winUtils.fullZoom;
+
       sendTouchpadScrollMotion(scrollbox, 1, ctrlKey, isMomentum);
+      winUtils.advanceTimeAndRefresh(1000); // force scrolling to happen
 
       setTimeout(function () {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         if (!ctrlKey) {
           let postfix = isMomentum ? ", even after releasing the touchpad" : "";
           // Normal scroll: scroll
           is(winUtils.fullZoom, zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix);
           isnot(scrollbox.scrollTop, scrollTopBefore, "Normal scrolling should scroll" + postfix);
@@ -78,16 +85,18 @@ function runTest() {
             is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad");
           } else {
             is(winUtils.fullZoom, zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl");
             isnot(scrollbox.scrollTop, scrollTopBefore, "Momentum scrolling should scroll, even when pressing Ctrl");
           }
         }
         // Revert the effect.
         sendTouchpadScrollMotion(scrollbox, -1, ctrlKey, isMomentum);
+        winUtils.advanceTimeAndRefresh(1000); // force scrolling to happen
+
         setTimeout(nextTest, 20);
       }, 20);
     }
     nextTest();
   }, win);
 }
 
 function initPrefs()
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -7,18 +7,18 @@
 #include "HTMLPropertiesCollection.h"
 #include "dombindings.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
-#include "mozilla/ErrorResult.h"
 #include "nsWrapperCacheInlines.h"
+#include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
 
 DOMCI_DATA(HTMLPropertiesCollection, mozilla::dom::HTMLPropertiesCollection)
 DOMCI_DATA(PropertyNodeList, mozilla::dom::PropertyNodeList)
 
 namespace mozilla {
 namespace dom {
 
 static PLDHashOperator
@@ -105,18 +105,24 @@ HTMLPropertiesCollection::SetDocument(ns
   mNamedItemEntries.EnumerateRead(SetPropertyListDocument, aDocument);
   mIsDirty = true;
 }
 
 JSObject*
 HTMLPropertiesCollection::WrapObject(JSContext* cx, JSObject* scope,
                                      bool* triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::HTMLPropertiesCollection::create(cx, scope, this,
-                                                                 triedToWrap);
+  JSObject* obj = HTMLPropertiesCollectionBinding::Wrap(cx, scope, this,
+                                                        triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::HTMLPropertiesCollection::create(cx, scope, this);
 }
 
 NS_IMETHODIMP
 HTMLPropertiesCollection::GetLength(uint32_t* aLength)
 {
   EnsureFresh();
   *aLength = mProperties.Length();
   return NS_OK;
@@ -441,18 +447,23 @@ PropertyNodeList::GetParentObject()
 {
   return mParent;
 }
 
 JSObject*
 PropertyNodeList::WrapObject(JSContext *cx, JSObject *scope,
                              bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::PropertyNodeList::create(cx, scope, this,
-                                                         triedToWrap);
+  JSObject* obj = PropertyNodeListBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::PropertyNodeList::create(cx, scope, this);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PropertyNodeList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PropertyNodeList)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
   tmp->SetDocument(nullptr);
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCollection)
--- a/content/html/content/src/nsClientRect.cpp
+++ b/content/html/content/src/nsClientRect.cpp
@@ -3,16 +3,17 @@
  * 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/. */
 
 #include "nsClientRect.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 
 #include "nsPresContext.h"
+#include "mozilla/dom/ClientRectListBinding.h"
 #include "dombindings.h"
 
 DOMCI_DATA(ClientRect, nsClientRect)
 
 NS_INTERFACE_TABLE_HEAD(nsClientRect)
   NS_INTERFACE_TABLE1(nsClientRect, nsIDOMClientRect)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ClientRect)
@@ -101,18 +102,24 @@ nsIDOMClientRect*
 nsClientRectList::GetItemAt(uint32_t aIndex)
 {
   return Item(aIndex);
 }
 
 JSObject*
 nsClientRectList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::ClientRectList::create(cx, scope, this,
-                                                       triedToWrap);
+  JSObject* obj = mozilla::dom::ClientRectListBinding::Wrap(cx, scope, this,
+                                                            triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::ClientRectList::create(cx, scope, this);
 }
 
 static double
 RoundFloat(double aValue)
 {
   return floor(aValue + 0.5);
 }
 
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -46,19 +46,19 @@
 #include "nsEventDispatcher.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsIHTMLCollection.h"
 
 #include "nsIConstraintValidation.h"
 
 #include "nsIDOMHTMLButtonElement.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
 #include "dombindings.h"
 #include "nsSandboxFlags.h"
-#include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla::dom;
 
 static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 16;
 
 static const uint8_t NS_FORM_AUTOCOMPLETE_ON  = 1;
 static const uint8_t NS_FORM_AUTOCOMPLETE_OFF = 0;
 
@@ -120,18 +120,23 @@ public:
    * @return NS_OK or NS_ERROR_OUT_OF_MEMORY.
    */
   nsresult GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const;
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
   {
-    return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
-                                                         triedToWrap);
+    JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
+    if (obj || *triedToWrap) {
+      return obj;
+    }
+
+    *triedToWrap = true;
+    return oldproxybindings::HTMLCollection::create(cx, scope, this);
   }
 
   nsHTMLFormElement* mForm;  // WEAK - the form owns me
 
   nsTArray<nsGenericHTMLFormElement*> mElements;  // Holds WEAK references - bug 36639
 
   // This array holds on to all form controls that are not contained
   // in mElements (form.elements in JS, see ShouldBeInFormControl()).
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -30,18 +30,18 @@
 #include "nsIFrame.h"
 
 #include "nsError.h"
 #include "nsServiceManagerUtils.h"
 #include "nsRuleData.h"
 #include "nsEventDispatcher.h"
 #include "mozilla/dom/Element.h"
 #include "mozAutoDocUpdate.h"
+#include "mozilla/dom/HTMLOptionsCollectionBinding.h"
 #include "dombindings.h"
-#include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS1(nsSelectState, nsSelectState)
 NS_DEFINE_STATIC_IID_ACCESSOR(nsSelectState, NS_SELECT_STATE_IID)
 
 //----------------------------------------------------------------------
@@ -2004,18 +2004,23 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHTMLOptionCollection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHTMLOptionCollection)
 
 
 JSObject*
 nsHTMLOptionCollection::WrapObject(JSContext *cx, JSObject *scope,
                                    bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::HTMLOptionsCollection::create(cx, scope, this,
-                                                              triedToWrap);
+  JSObject* obj = HTMLOptionsCollectionBinding::Wrap(cx, scope, this, triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return oldproxybindings::HTMLOptionsCollection::create(cx, scope, this);
 }
 
 NS_IMETHODIMP
 nsHTMLOptionCollection::GetLength(uint32_t* aLength)
 {
   *aLength = mElements.Length();
 
   return NS_OK;
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -20,19 +20,18 @@
 #include "nsHTMLParts.h"
 #include "nsRuleData.h"
 #include "nsStyleContext.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsIDOMElement.h"
 #include "nsIHTMLCollection.h"
 #include "nsHTMLStyleSheet.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
 #include "dombindings.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla;
 
 /* ------------------------------ TableRowsCollection -------------------------------- */
 /**
  * This class provides a late-bound collection of rows in a table.
  * mParent is NOT ref-counted to avoid circular references
  */
@@ -57,18 +56,25 @@ public:
   NS_IMETHOD    ParentDestroyed();
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
   {
-    return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
-                                                         triedToWrap);
+    JSObject* obj = mozilla::dom::HTMLCollectionBinding::Wrap(cx, scope, this,
+                                                              triedToWrap);
+    if (obj || *triedToWrap) {
+      return obj;
+    }
+
+    *triedToWrap = true;
+    return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope,
+                                                                  this);
   }
 
 protected:
   // Those rows that are not in table sections
   nsHTMLTableElement* mParent;
   nsRefPtr<nsContentList> mOrphanRows;  
 };
 
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -5,16 +5,17 @@
 
 #include "nsSVGElement.h"
 #include "DOMSVGLengthList.h"
 #include "DOMSVGLength.h"
 #include "nsError.h"
 #include "SVGAnimatedLengthList.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/SVGLengthListBinding.h"
 #include "dombindings.h"
 
 // See the comment in this file's header.
 
 // local helper functions
 namespace {
 
 using mozilla::DOMSVGLength;
@@ -71,18 +72,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLengthList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLengthList)
 NS_INTERFACE_MAP_END
 
 JSObject*
 DOMSVGLengthList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::SVGLengthList::create(cx, scope, this,
-                                                      triedToWrap);
+  JSObject* obj = mozilla::dom::SVGLengthListBinding::Wrap(cx, scope, this,
+                                                           triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::SVGLengthList::create(cx, scope, this);
 }
 
 nsIDOMSVGLength*
 DOMSVGLengthList::GetItemAt(uint32_t aIndex)
 {
   ErrorResult rv;
   return GetItem(aIndex, rv);
 }
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -5,16 +5,17 @@
 
 #include "nsSVGElement.h"
 #include "DOMSVGNumberList.h"
 #include "DOMSVGNumber.h"
 #include "nsError.h"
 #include "SVGAnimatedNumberList.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/SVGNumberListBinding.h"
 #include "dombindings.h"
 
 // See the comment in this file's header.
 
 namespace mozilla {
 
 // local helper functions
 namespace {
@@ -72,18 +73,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGNumberList)
 NS_INTERFACE_MAP_END
 
 
 JSObject*
 DOMSVGNumberList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::SVGNumberList::create(cx, scope, this,
-                                                      triedToWrap);
+  JSObject* obj = mozilla::dom::SVGNumberListBinding::Wrap(cx, scope, this,
+                                                           triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::SVGNumberList::create(cx, scope, this);
 }
 
 nsIDOMSVGNumber*
 DOMSVGNumberList::GetItemAt(uint32_t aIndex)
 {
   ErrorResult rv;
   return GetItem(aIndex, rv);
 }
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -6,16 +6,17 @@
 #include "nsSVGElement.h"
 #include "DOMSVGPathSegList.h"
 #include "DOMSVGPathSeg.h"
 #include "nsError.h"
 #include "SVGAnimatedPathSegList.h"
 #include "nsCOMPtr.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "SVGPathSegUtils.h"
+#include "mozilla/dom/SVGPathSegListBinding.h"
 #include "dombindings.h"
 #include "nsContentUtils.h"
 
 // See the comment in this file's header.
 
 namespace mozilla {
 
 static nsSVGAttrTearoffTable<void, DOMSVGPathSegList>
@@ -78,18 +79,25 @@ DOMSVGPathSegList::~DOMSVGPathSegList()
     InternalAList().GetAnimValKey() :
     InternalAList().GetBaseValKey();
   sSVGPathSegListTearoffTable.RemoveTearoff(key);
 }
 
 JSObject*
 DOMSVGPathSegList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::SVGPathSegList::create(cx, scope, this,
-                                                       triedToWrap);
+  JSObject* obj = mozilla::dom::SVGPathSegListBinding::Wrap(cx, scope, this,
+                                                            triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::SVGPathSegList::create(cx, scope,
+                                                                this);
 }
 
 nsIDOMSVGPathSeg*
 DOMSVGPathSegList::GetItemAt(uint32_t aIndex)
 {
   ErrorResult rv;
   return GetItem(aIndex, rv);
 }
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -6,16 +6,17 @@
 #include "nsSVGElement.h"
 #include "DOMSVGPointList.h"
 #include "DOMSVGPoint.h"
 #include "nsError.h"
 #include "SVGAnimatedPointList.h"
 #include "nsCOMPtr.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/SVGPointListBinding.h"
 #include "dombindings.h"
 
 // See the comment in this file's header.
 
 // local helper functions
 namespace {
 
 using mozilla::DOMSVGPoint;
@@ -97,18 +98,24 @@ DOMSVGPointList::~DOMSVGPointList()
     InternalAList().GetAnimValKey() :
     InternalAList().GetBaseValKey();
   sSVGPointListTearoffTable.RemoveTearoff(key);
 }
 
 JSObject*
 DOMSVGPointList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::SVGPointList::create(cx, scope, this,
-                                                     triedToWrap);
+  JSObject* obj = mozilla::dom::SVGPointListBinding::Wrap(cx, scope, this,
+                                                          triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::SVGPointList::create(cx, scope, this);
 }
 
 nsIDOMSVGPoint*
 DOMSVGPointList::GetItemAt(uint32_t aIndex)
 {
   if (IsAnimValList()) {
     Element()->FlushAnimations();
   }
--- a/content/svg/content/src/DOMSVGTransformList.cpp
+++ b/content/svg/content/src/DOMSVGTransformList.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGTransformList.h"
 #include "DOMSVGTransform.h"
 #include "DOMSVGMatrix.h"
 #include "SVGAnimatedTransformList.h"
 #include "nsSVGElement.h"
 #include "nsContentUtils.h"
+#include "mozilla/dom/SVGTransformListBinding.h"
 #include "dombindings.h"
 #include "nsError.h"
 
 // local helper functions
 namespace {
 
 void UpdateListIndicesFromIndex(
   nsTArray<mozilla::DOMSVGTransform*>& aItemsArray,
@@ -72,18 +73,25 @@ NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // DOMSVGTransformList methods:
 
 JSObject*
 DOMSVGTransformList::WrapObject(JSContext *cx, JSObject *scope,
                                 bool *triedToWrap)
 {
-  return mozilla::dom::oldproxybindings::SVGTransformList::create(cx, scope, this,
-                                                         triedToWrap);
+  JSObject* obj = mozilla::dom::SVGTransformListBinding::Wrap(cx, scope, this,
+                                                              triedToWrap);
+  if (obj || *triedToWrap) {
+    return obj;
+  }
+
+  *triedToWrap = true;
+  return mozilla::dom::oldproxybindings::SVGTransformList::create(cx, scope,
+                                                                  this);
 }
 
 nsIDOMSVGTransform*
 DOMSVGTransformList::GetItemAt(uint32_t aIndex)
 {
   ErrorResult rv;
   return GetItem(aIndex, rv);
 }
--- a/content/svg/content/test/test_SVGxxxListIndexing.xhtml
+++ b/content/svg/content/test/test_SVGxxxListIndexing.xhtml
@@ -20,22 +20,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 var text = document.getElementById("text"),
     path = document.getElementById("path"),
     poly = document.getElementById("poly");
     g    = document.getElementById("g");
 
 function CheckList(aListObject, aExpectedListLength, aListDescription)
 {
   is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".numberOfItems");
-  is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".length");
+  is(aListObject.length, aExpectedListLength, aListDescription + ".length");
   for (let i = 0; i < aListObject.length; i++) {
     let item = aListObject.getItem(i);
     ok(aListObject[i] === item, aListDescription + "[" + i + "]");
   }
-  ok(aListObject[aListObject.length] === void 0, aListDescription + "[outOfBounds]");
+  is(typeof(aListObject[aListObject.length]), "undefined", aListDescription + "[outOfBounds]");
 }
 
 var tests = [
   { element: text,
     attribute: "x",
     listProperty: "x.baseVal",
     type: "SVGLengthList",
     subtests: [ { values: null, length: 3 },
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -38,16 +38,17 @@
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsTHashtable.h"
 
 #include "nsIScriptContext.h"
 #include "nsBindingManager.h"
 
 #include "nsThreadUtils.h"
+#include "mozilla/dom/NodeListBinding.h"
 #include "dombindings.h"
 
 // ==================================================================
 // = nsAnonymousContentList 
 // ==================================================================
 
 #define NS_ANONYMOUS_CONTENT_LIST_IID \
   { 0xbfb5d8e7, 0xf718, 0x4a46, \
@@ -74,18 +75,24 @@ public:
   int32_t GetInsertionPointCount() { return mElements->Length(); }
 
   nsXBLInsertionPoint* GetInsertionPointAt(int32_t i) { return static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i)); }
   void RemoveInsertionPointAt(int32_t i) { mElements->RemoveElementAt(i); }
 
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
   {
-    return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this,
-                                                   triedToWrap);
+    JSObject* obj = mozilla::dom::NodeListBinding::Wrap(cx, scope, this,
+                                                        triedToWrap);
+    if (obj || *triedToWrap) {
+      return obj;
+    }
+
+    *triedToWrap = true;
+    return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this);
   }
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID)
 private:
   nsCOMPtr<nsIContent> mContent;
   nsInsertionPointList* mElements;
 };
 
--- a/dbm/include/mcom_db.h
+++ b/dbm/include/mcom_db.h
@@ -201,19 +201,23 @@
 #endif
 #endif
 
 #ifdef macintosh
 #include <stdio.h>
 #include "xp_mcom.h"
 #define O_ACCMODE       3       /* Mask for file access modes */
 #define EFTYPE 2000
-PR_BEGIN_EXTERN_C
+#ifdef __cplusplus
+extern "C" {
+#endif
 int mkstemp(const char *path);
-PR_END_EXTERN_C
+#ifdef __cplusplus
+}
+#endif
 #endif	/* MACINTOSH */
 
 #if !defined(_WINDOWS) && !defined(macintosh)
 #include <sys/stat.h>
 #include <errno.h>
 #endif
 
 /* define EFTYPE since most don't */
@@ -386,28 +390,32 @@ typedef struct {
 	((char *)a)[1] = ((char *)&_tmp)[0];				\
 }
 #define	P_16_COPY(a, b) {						\
 	((char *)&(b))[0] = ((char *)&(a))[1];				\
 	((char *)&(b))[1] = ((char *)&(a))[0];				\
 }
 #endif
 
-PR_BEGIN_EXTERN_C
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 extern DB *
 dbopen (const char *, int, int, DBTYPE, const void *);
 
 /* set or unset a global lock flag to disable the
  * opening of any DBM file
  */
 void dbSetOrClearDBLock(DBLockFlagEnum type);
 
 #ifdef __DBINTERFACE_PRIVATE
 DB	*__bt_open (const char *, int, int, const BTREEINFO *, int);
 DB	*__hash_open (const char *, int, int, const HASHINFO *, int);
 DB	*__rec_open (const char *, int, int, const RECNOINFO *, int);
 void	 __dbpanic (DB *dbp);
 #endif
 
-PR_END_EXTERN_C
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* !_DB_H_ */
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -182,18 +182,16 @@ WebappsRegistry.prototype = {
   get mgmt() {
     if (!this._mgmt)
       this._mgmt = new WebappsApplicationMgmt(this._window);
     return this._mgmt;
   },
 
   uninit: function() {
     this._mgmt = null;
-    cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
-                          ["Webapps:Install:Return:OK"]);
   },
 
   // mozIDOMApplicationRegistry2 implementation
 
   installPackage: function(aURL, aParams) {
     let installURL = this._window.location.href;
     let installOrigin = this._getOrigin(installURL);
     this._validateScheme(aURL);
@@ -479,19 +477,16 @@ WebappsApplication.prototype = {
       BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
     if (browserChild) {
       browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData");
     }
   },
 
   uninit: function() {
     this._onprogress = null;
-    cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
-                          ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache",
-                           "Webapps:PackageEvent"]);
   },
 
   _fireEvent: function(aName, aHandler) {
     if (aHandler) {
       let event = new this._window.MozApplicationEvent(aName, { application: this });
       aHandler.handleEvent(event);
     }
   },
@@ -644,18 +639,16 @@ WebappsApplicationMgmt.prototype = {
                       getNotInstalled: "r",
                       oninstall: "rw",
                       onuninstall: "rw"
                      },
 
   uninit: function() {
     this._oninstall = null;
     this._onuninstall = null;
-    cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
-                          ["Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]);
   },
 
   applyDownload: function(aApp) {
     if (!aApp.readyToApplyDownload) {
       return;
     }
 
     cpmm.sendAsyncMessage("Webapps::ApplyDownload",
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -154,19 +154,19 @@ let DOMApplicationRegistry = {
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
                      "Webapps:GetSelf", "Webapps:IsInstalled",
                      "Webapps:GetInstalled", "Webapps:GetNotInstalled",
                      "Webapps:Launch", "Webapps:GetAll",
                      "Webapps:InstallPackage", "Webapps:GetBasePath",
                      "Webapps:GetList", "Webapps:RegisterForMessages",
-                     "Webapps:UnregisterForMessages",
                      "Webapps:CancelDownload", "Webapps:CheckForUpdate",
-                     "Webapps::Download", "Webapps::ApplyDownload"];
+                     "Webapps::Download", "Webapps::ApplyDownload",
+                     "child-process-shutdown"];
 
     this.frameMessages = ["Webapps:ClearBrowserData"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     cpmm.addMessageListener("Activities:Register:OK", this);
@@ -507,27 +507,27 @@ let DOMApplicationRegistry = {
     aMsgNames.forEach(function (aMsgName) {
       if (!(aMsgName in this.children)) {
         this.children[aMsgName] = [];
       }
       this.children[aMsgName].push(aMm);
     }, this);
   },
 
-  removeMessageListener: function(aMsgNames, aMm) {
-    aMsgNames.forEach(function (aMsgName) {
-      if (!(aMsgName in this.children)) {
-        return;
-      }
+
+  removeMessageListener: function(aMm) {
+    for (let i = this.children.length - 1; i >= 0; i -= 1) {
+      msg = this.children[i];
 
       let index;
-      if ((index = this.children[aMsgName].indexOf(aMm)) != -1) {
-        this.children[aMsgName].splice(index, 1);
+      if ((index = msg.indexOf(aMm)) != -1) {
+         debug("Remove dead mm at index " + index);
+         msg.splice(index, 1);
       }
-    }, this);
+    };
   },
 
   receiveMessage: function(aMessage) {
     // nsIPrefBranch throws if pref does not exist, faster to simply write
     // the pref instead of first checking if it is false.
     Services.prefs.setBoolPref("dom.mozApps.used", true);
 
     // We need to check permissions for calls coming from mozApps.mgmt.
@@ -537,17 +537,17 @@ let DOMApplicationRegistry = {
          "Webapps::ApplyDownload"].indexOf(aMessage.name) != -1) {
       if (!aMessage.target.assertPermission("webapps-manage")) {
         debug("mozApps message " + aMessage.name +
         " from a content process with no 'webapps-manage' privileges.");
         return null;
       }
     }
 
-    let msg = aMessage.json;
+    let msg = aMessage.data || {};
     let mm = aMessage.target;
     msg.mm = mm;
 
     switch (aMessage.name) {
       case "Webapps:Install":
         // always ask for UI to install
         Services.obs.notifyObservers(mm, "webapps-ask-install", JSON.stringify(msg));
         break;
@@ -580,19 +580,16 @@ let DOMApplicationRegistry = {
         this.installPackage(msg, mm);
         break;
       case "Webapps:GetBasePath":
         return this.webapps[msg.id].basePath;
         break;
       case "Webapps:RegisterForMessages":
         this.addMessageListener(msg, mm);
         break;
-      case "Webapps:UnregisterForMessages":
-        this.removeMessageListener(msg, mm);
-        break;
       case "Webapps:GetList":
         this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], mm);
         return this.webapps;
       case "Webapps:Download":
         this.startDownload(msg.manifestURL);
         break;
       case "Webapps:CancelDownload":
         this.cancelDownload(msg.manifestURL);
@@ -605,16 +602,19 @@ let DOMApplicationRegistry = {
         break;
       case "Activities:Register:OK":
         this.activitiesRegistered++;
         if (this.allActivitiesSent &&
             this.activitiesRegistered === this.activitiesToRegister) {
           this.onInitDone();
         }
         break;
+      case "child-process-shutdown":
+        this.removeMessageListener(mm);
+        break;
     }
   },
 
   // Some messages can be listened by several content processes:
   // Webapps:AddApp
   // Webapps:RemoveApp
   // Webapps:Install:Return:OK
   // Webapps:Uninstall:Return:OK
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -549,16 +549,17 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "GeneratedEvents.h"
 #include "mozilla/Likely.h"
 #include "nsDebug.h"
 
 #undef None // something included above defines this preprocessor symbol, maybe Xlib headers
 #include "WebGLContext.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/HTMLCollectionBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -278,33 +278,17 @@ nsScreen::Notify(const hal::ScreenConfig
 
   NS_ASSERTION(mOrientation == eScreenOrientation_PortraitPrimary ||
                mOrientation == eScreenOrientation_PortraitSecondary ||
                mOrientation == eScreenOrientation_LandscapePrimary ||
                mOrientation == eScreenOrientation_LandscapeSecondary,
                "Invalid orientation value passed to notify method!");
 
   if (mOrientation != previousOrientation) {
-    // TODO: use an helper method, see bug 720768.
-    nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-    nsresult rv = event->InitEvent(NS_LITERAL_STRING("mozorientationchange"), false, false);
-    if (NS_FAILED(rv)) {
-      return;
-    }
-
-    rv = event->SetTrusted(true);
-    if (NS_FAILED(rv)) {
-      return;
-    }
-
-    bool dummy;
-    rv = DispatchEvent(event, &dummy);
-    if (NS_FAILED(rv)) {
-      return;
-    }
+    DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange"));
   }
 }
 
 NS_IMETHODIMP
 nsScreen::GetMozOrientation(nsAString& aOrientation)
 {
   switch (mOrientation) {
     case eScreenOrientation_PortraitPrimary:
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -113,33 +113,16 @@ BatteryManager::GetChargingTime(double* 
     return NS_OK;
   }
 
   *aChargingTime = mRemainingTime;
 
   return NS_OK;
 }
 
-nsresult
-BatteryManager::DispatchTrustedEventToSelf(const nsAString& aEventName)
-{
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(aEventName, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 void
 BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo)
 {
   mLevel = aBatteryInfo.level();
   mCharging = aBatteryInfo.charging();
   mRemainingTime = aBatteryInfo.remainingTime();
 
   // Add some guards to make sure the values are coherent.
@@ -156,44 +139,44 @@ BatteryManager::Notify(const hal::Batter
 {
   double previousLevel = mLevel;
   bool previousCharging = mCharging;
   double previousRemainingTime = mRemainingTime;
 
   UpdateFromBatteryInfo(aBatteryInfo);
 
   if (previousCharging != mCharging) {
-    DispatchTrustedEventToSelf(CHARGINGCHANGE_EVENT_NAME);
+    DispatchTrustedEvent(CHARGINGCHANGE_EVENT_NAME);
   }
 
   if (previousLevel != mLevel) {
-    DispatchTrustedEventToSelf(LEVELCHANGE_EVENT_NAME);
+    DispatchTrustedEvent(LEVELCHANGE_EVENT_NAME);
   }
 
   /*
    * There are a few situations that could happen here:
    * 1. Charging state changed:
    *   a. Previous remaining time wasn't unkwonw, we have to fire an event for
    *      the change.
    *   b. New remaining time isn't unkwonw, we have to fire an event for it.
    * 2. Charging state didn't change but remainingTime did, we have to fire
    *    the event that correspond to the current charging state.
    */
   if (mCharging != previousCharging) {
     if (previousRemainingTime != kUnknownRemainingTime) {
-      DispatchTrustedEventToSelf(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME
-                                                  : DISCHARGINGTIMECHANGE_EVENT_NAME);
+      DispatchTrustedEvent(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                            : DISCHARGINGTIMECHANGE_EVENT_NAME);
     }
     if (mRemainingTime != kUnknownRemainingTime) {
-      DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
-                                           : DISCHARGINGTIMECHANGE_EVENT_NAME);
+      DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                     : DISCHARGINGTIMECHANGE_EVENT_NAME);
     }
   } else if (previousRemainingTime != mRemainingTime) {
-    DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
-                                         : DISCHARGINGTIMECHANGE_EVENT_NAME);
+    DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
+                                   : DISCHARGINGTIMECHANGE_EVENT_NAME);
   }
 }
 
 /* static */ bool
 BatteryManager::HasSupport()
 {
   return Preferences::GetBool("dom.battery.enabled", true);
 }
--- a/dom/battery/BatteryManager.h
+++ b/dom/battery/BatteryManager.h
@@ -49,21 +49,16 @@ public:
    * Returns whether the battery api is supported (ie. not disabled by the user)
    * @return whether the battery api is supported.
    */
   static bool HasSupport();
 
 
 private:
   /**
-   * Dispatch a trusted non-cancellable and non-bubbling event to itself.
-   */
-  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
-
-  /**
    * Update the battery information stored in the battery manager object using
    * a battery information object.
    */
   void UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo);
 
   double mLevel;
   bool   mCharging;
   /**
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/ErrorResult.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 
 #include "nsIXPConnect.h"
@@ -1140,12 +1141,33 @@ XrayEnumerateProperties(JS::AutoIdVector
                         jsid* attributeIds,
                         JSPropertySpec* attributeSpecs,
                         size_t attributeCount,
                         Prefable<ConstantSpec>* constants,
                         jsid* constantIds,
                         ConstantSpec* constantSpecs,
                         size_t constantCount);
 
+// Transfer reference in ptr to smartPtr.
+template<class T>
+inline void
+Take(nsRefPtr<T>& smartPtr, T* ptr)
+{
+  smartPtr = dont_AddRef(ptr);
+}
+
+// Transfer ownership of ptr to smartPtr.
+template<class T>
+inline void
+Take(nsAutoPtr<T>& smartPtr, T* ptr)
+{
+  smartPtr = ptr;
+}
+
+inline void
+MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
+{
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -49,50 +49,58 @@
 #                   dict). The keys are the property names as they appear in the
 #                   .webidl file and the values are the names as they should be
 #                   in the WebIDL.
 #   * wrapperCache: True if this object is a wrapper cache.  Objects that are
 #                   not can only be returned from a limited set of methods,
 #                   cannot be prefable, and must ensure that they disallow
 #                   XPConnect wrapping.  Always true for worker descriptors.
 #                   Defaults to true.
+#   * nativeOwnership: Describes how the native object is held. 4 possible
+#                      types: worker object ("worker"), non-refcounted object
+#                      ("owned"), refcounted non-nsISupports object
+#                      ("refcounted") or nsISupports ("nsisupports").
+#                      Non-refcounted objects need to inherit from
+#                      mozilla::dom::NonRefcountedDOMObject and preferably use
+#                      MOZ_COUNT_CTOR/MOZ_COUNT_DTOR in their
+#                      constructor/destructor so they participate in leak
+#                      logging.
+#                      This mostly determines how the finalizer releases the
+#                      binding's hold on the native object. For a worker object
+#                      it'll call Release, for a non-refcounted object it'll
+#                      call delete through XPConnect's deferred finalization
+#                      mechanism, for a refcounted object it'll call Release
+#                      through XPConnect's deferred finalization mechanism.
+#                      Always "worker" for worker descriptors. Defaults to
+#                      "nsisupports".
 #
 #   The following fields are either a string, an array (defaults to an empty
 #   array) or a dictionary with three possible keys (all, getterOnly and
 #   setterOnly) each having such an array as the value
 #
 #   * implicitJSContext - attributes and methods specified in the .webidl file
 #                         that require a JSContext as the first argument
 #   * resultNotAddRefed - attributes and methods specified in the .webidl file
 #                         that do not AddRef the return value
 
 DOMInterfaces = {
 
-'AudioBuffer' : {
-},
-
 'mozAudioContext': {
     'nativeType': 'AudioContext',
     'implicitJSContext': [ 'createBuffer' ],
 },
 
 'AudioNode' : {
     'concrete': False,
 },
 
 'AudioSourceNode': {
     'concrete': False,
 },
 
-'AudioBufferSourceNode': {
-},
-
-'AudioDestinationNode': {
-},
-
 'Blob': [
 {
     'headerFile': 'nsIDOMFile.h',
 },
 {
     'workers': True,
 }],
 
@@ -154,59 +162,50 @@ DOMInterfaces = {
     'prefable': True,
     'binaryNames': {
         '__stringifier': 'Stringify'
     }
 }],
 
 'Event': [
 {
-},
-{
     'workers': True,
 }],
 
 'EventListener': [
 {
-},
-{
     'workers': True,
 }],
 
 'EventTarget': [
 {
     'nativeType': 'nsDOMEventTargetHelper',
     'hasInstanceInterface': 'nsIDOMEventTarget',
     'concrete': False,
     'prefable': True,
 },
 {
     'workers': True,
-    'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h',
     'concrete': False
 }],
 
 'FileList': [
 {
     'nativeType': 'nsDOMFileList',
     'headerFile': 'nsDOMFile.h',
     'prefable': True,
     'resultNotAddRefed': [ 'item' ]
 }],
 
-'FileReaderSync': [
-{
+'FileReaderSync': {
     'workers': True,
-    'headerFile': 'mozilla/dom/workers/bindings/FileReaderSync.h'
-}],
+},
 
 'FormData': [
 {
-},
-{
     'workers': True,
 }],
 
 'HTMLCollection': [
 {
     'nativeType': 'nsIHTMLCollection',
     'prefable': True,
     'resultNotAddRefed': [ 'item' ]
@@ -360,41 +359,38 @@ DOMInterfaces = {
 'XMLHttpRequest': [
 {
     'nativeType': 'nsXMLHttpRequest',
     'implicitJSContext': [ 'constructor', ],
     'resultNotAddRefed': [ 'upload', 'responseXML' ]
 },
 {
     'workers': True,
-    'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequest.h',
 }],
 
 'XMLHttpRequestEventTarget': [
 {
     'nativeType': 'nsXHREventTarget',
     'headerFile': 'nsXMLHttpRequest.h',
     'concrete': False,
     'prefable': True,
 },
 {
     'workers': True,
     'concrete': False,
-    'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestEventTarget.h'
 }],
 
 'XMLHttpRequestUpload': [
 {
     'nativeType': 'nsXMLHttpRequestUpload',
     'headerFile': 'nsXMLHttpRequest.h',
     'prefable': True
 },
 {
     'workers': True,
-    'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestUpload.h'
 }],
 
 'WebSocket': [
 {
     'headerFile': 'WebSocket.h',
     'implicitJSContext': [ 'constructor' ]
 }],
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -91,17 +91,17 @@ def DOMClass(descriptor):
         # is never the ID of any prototype, so it's safe to use as
         # padding.
         protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
         prototypeChainString = ', '.join(protoList)
         nativeHooks = "NULL" if descriptor.workers else "&NativeHooks"
         return """{
   { %s },
   %s, %s
-}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports),
+}""" % (prototypeChainString, toStringBool(descriptor.nativeOwnership == 'nsisupports'),
           nativeHooks)
 
 class CGDOMJSClass(CGThing):
     """
     Generate a DOMJSClass for a given descriptor
     """
     def __init__(self, descriptor):
         CGThing.__init__(self)
@@ -629,36 +629,106 @@ class CGAddPropertyHook(CGAbstractClassH
                 Argument('JSHandleId', 'id'), Argument('JSMutableHandleValue', 'vp')]
         CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
                                      'JSBool', args)
 
     def generate_code(self):
         # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=774279
         # Using a real trace hook might enable us to deal with non-nsISupports
         # wrappercached things here.
-        assert self.descriptor.nativeIsISupports
+        assert self.descriptor.nativeOwnership == 'nsisupports'
         return """  nsContentUtils::PreserveWrapper(reinterpret_cast<nsISupports*>(self), self);
   return true;"""
 
+def DeferredFinalizeSmartPtr(descriptor):
+    if descriptor.nativeOwnership == 'owned':
+        smartPtr = 'nsAutoPtr<%s>'
+    else:
+        assert descriptor.nativeOwnership == 'refcounted'
+        smartPtr = 'nsRefPtr<%s>'
+    return smartPtr % descriptor.nativeType
+
+class CGDeferredFinalizePointers(CGThing):
+    def __init__(self, descriptor):
+        CGThing.__init__(self)
+        self.descriptor = descriptor
+
+    def declare(self):
+        return ""
+
+    def define(self):
+        return """nsTArray<%s >* sDeferredFinalizePointers;
+""" % DeferredFinalizeSmartPtr(self.descriptor)
+
+class CGGetDeferredFinalizePointers(CGAbstractStaticMethod):
+    def __init__(self, descriptor):
+        CGAbstractStaticMethod.__init__(self, descriptor, "GetDeferredFinalizePointers", "void*", [])
+
+    def definition_body(self):
+        return """  nsTArray<%s >* pointers = sDeferredFinalizePointers;
+  sDeferredFinalizePointers = nullptr;
+  return pointers;""" % DeferredFinalizeSmartPtr(self.descriptor)
+
+class CGDeferredFinalize(CGAbstractStaticMethod):
+    def __init__(self, descriptor):
+        CGAbstractStaticMethod.__init__(self, descriptor, "DeferredFinalize", "bool", [Argument('int32_t', 'slice'), Argument('void*', 'data')])
+
+    def definition_body(self):
+        smartPtr = DeferredFinalizeSmartPtr(self.descriptor)
+        return """  nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
+  uint32_t oldLen = pointers->Length();
+  if (slice == -1 || slice > oldLen) {
+    slice = oldLen;
+  }
+  uint32_t newLen = oldLen - slice;
+  pointers->RemoveElementsAt(newLen, slice);
+  if (newLen == 0) {
+    delete pointers;
+    return true;
+  }
+  return false;""" % { 'smartPtr': smartPtr }
+
 def finalizeHook(descriptor, hookName, context):
     if descriptor.customFinalize:
         return """if (self) {
   self->%s(%s);
 }""" % (hookName, context)
     clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
     if descriptor.workers:
         release = "self->Release();"
-    else:
-        assert descriptor.nativeIsISupports
+    elif descriptor.nativeOwnership == 'nsisupports':
         release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
 if (rt) {
   rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
 } else {
   NS_RELEASE(self);
 }"""
+    else:
+        smartPtr = DeferredFinalizeSmartPtr(descriptor)
+        release = """static bool registered = false;
+if (!registered) {
+  XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
+  if (!rt) {
+    %(smartPtr)s dying;
+    Take(dying, self);
+    return;
+  }
+  rt->RegisterDeferredFinalize(GetDeferredFinalizePointers, DeferredFinalize);
+  registered = true;
+}
+if (!sDeferredFinalizePointers) {
+  sDeferredFinalizePointers = new nsAutoTArray<%(smartPtr)s, 16>();
+}
+%(smartPtr)s* defer = sDeferredFinalizePointers->AppendElement();
+if (!defer) {
+  %(smartPtr)s dying;
+  Take(dying, self);
+  return;
+}
+Take(*defer, self);""" % { 'smartPtr': smartPtr }
     return clearWrapper + release
 
 class CGClassFinalizeHook(CGAbstractClassHook):
     """
     A hook for finalize, used to release our native object.
     """
     def __init__(self, descriptor):
         args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
@@ -738,16 +808,17 @@ class CGClassHasInstanceHook(CGAbstractS
         if not self.descriptor.hasInstanceInterface:
             return ""
         return CGAbstractStaticMethod.define(self)
 
     def definition_body(self):
         return self.generate_code()
 
     def generate_code(self):
+        assert self.descriptor.nativeOwnership == 'nsisupports'
         return """  if (!vp.isObject()) {
     *bp = false;
     return true;
   }
 
   jsval protov;
   if (!JS_GetProperty(cx, obj, "prototype", &protov))
     return false;
@@ -956,17 +1027,17 @@ class MethodDefiner(PropertyDefiner):
                                 "pref": None })
             self.regular.append({"name": 'iterator',
                                  "methodInfo": False,
                                  "nativeName": "JS_ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "pref": None })
 
-        if not descriptor.interface.parent and not static and not descriptor.workers:
+        if not descriptor.interface.parent and not static and descriptor.nativeOwnership == 'nsisupports':
             self.chrome.append({"name": 'QueryInterface',
                                 "methodInfo": False,
                                 "length": 1,
                                 "flags": "0",
                                 "pref": None })
 
         if static:
             if not descriptor.interface.hasInterfaceObject():
@@ -1381,16 +1452,25 @@ def CreateBindingJSObject(descriptor, pa
     else:
         create = """  JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
   if (!obj) {
     return NULL;
   }
 
   js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
 """
+    if descriptor.nativeOwnership in ['refcounted', 'nsisupports']:
+        create += """  NS_ADDREF(aObject);
+"""
+    else:
+        assert descriptor.nativeOwnership == 'owned'
+        create += """  // Make sure the native objects inherit from NonRefcountedDOMObject so that we
+  // log their ctor and dtor.
+  MustInheritFromNonRefcountedDOMObject(aObject);
+"""
     return create % parent
 
 class CGWrapWithCacheMethod(CGAbstractMethod):
     def __init__(self, descriptor):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
                 Argument(descriptor.nativeType + '*', 'aObject'),
                 Argument('nsWrapperCache*', 'aCache'),
@@ -1413,17 +1493,16 @@ class CGWrapWithCacheMethod(CGAbstractMe
   JSObject* global = JS_GetGlobalForObject(aCx, parent);
 %s
   JSObject* proto = GetProtoObject(aCx, global, global);
   if (!proto) {
     return NULL;
   }
 
 %s
-  NS_ADDREF(aObject);
 
   aCache->SetWrapper(obj);
 
   return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"),
                     CreateBindingJSObject(self.descriptor, "parent"))
 
 class CGWrapMethod(CGAbstractMethod):
     def __init__(self, descriptor):
@@ -1448,17 +1527,16 @@ class CGWrapNonWrapperCacheMethod(CGAbst
         return """
   JSObject* global = JS_GetGlobalForObject(aCx, aScope);
   JSObject* proto = GetProtoObject(aCx, global, global);
   if (!proto) {
     return NULL;
   }
 
 %s
-  NS_ADDREF(aObject);
 
   return obj;""" % CreateBindingJSObject(self.descriptor, "global")
 
 builtinNames = {
     IDLType.Tags.bool: 'bool',
     IDLType.Tags.int8: 'int8_t',
     IDLType.Tags.int16: 'int16_t',
     IDLType.Tags.int32: 'int32_t',
@@ -3897,20 +3975,17 @@ class CGUnionStruct(CGThing):
         enumValues.extend(mapTemplate("e${name}", templateVars))
         methodTemplate = """  bool Is${name}() const
   {
     return mType == e${name};
   }
   ${externalType} GetAs${name}() const
   {
     MOZ_ASSERT(Is${name}(), "Wrong type!");
-    // The cast to ${externalType} is needed to work around a bug in Apple's
-    // clang compiler, for some reason it doesn't call |S::operator T&| when
-    // casting S<T> to T& and T is forward declared.
-    return (${externalType})mValue.m${name}.Value();
+    return const_cast<${structType}&>(mValue.m${name}.Value());
   }
   ${structType}& SetAs${name}()
   {
     mType = e${name};
     return mValue.m${name}.SetValue();
   }"""
         methods.extend(mapTemplate(methodTemplate, templateVars))
         values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
@@ -4782,17 +4857,16 @@ class CGDOMJSProxyHandler_defineProperty
                     "                              eStringify, eStringify, name)) {\n" +
                     "    return false;\n" +
                     "  }\n" +
                     "  %s* self = UnwrapProxy(proxy);\n" +
                     CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
                     "  if (found) {\n"
                     "    return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
                     "  }\n" +
-                    "  return true;\n"
                     "}\n") % (self.descriptor.nativeType, self.descriptor.name)
         return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
 
 class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('JS::AutoIdVector&', 'props')]
         ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
@@ -5100,27 +5174,33 @@ class CGDescriptor(CGThing):
             if hasMethod: cgThings.append(CGGenericMethod(descriptor))
             if hasGetter: cgThings.append(CGGenericGetter(descriptor))
             if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
                                                                  lenientThis=True))
             if hasSetter: cgThings.append(CGGenericSetter(descriptor))
             if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
                                                                  lenientThis=True))
 
-        if descriptor.concrete and not descriptor.proxy:
-            if not descriptor.workers and descriptor.wrapperCache:
-                cgThings.append(CGAddPropertyHook(descriptor))
-
-            # Always have a finalize hook, regardless of whether the class wants a
-            # custom hook.
-            cgThings.append(CGClassFinalizeHook(descriptor))
-
-            # Only generate a trace hook if the class wants a custom hook.
-            if (descriptor.customTrace):
-                cgThings.append(CGClassTraceHook(descriptor))
+        if descriptor.concrete:
+            if descriptor.nativeOwnership == 'owned' or descriptor.nativeOwnership == 'refcounted':
+                cgThings.append(CGDeferredFinalizePointers(descriptor))
+                cgThings.append(CGGetDeferredFinalizePointers(descriptor))
+                cgThings.append(CGDeferredFinalize(descriptor))
+
+            if not descriptor.proxy:
+                if not descriptor.workers and descriptor.wrapperCache:
+                    cgThings.append(CGAddPropertyHook(descriptor))
+
+                # Always have a finalize hook, regardless of whether the class
+                # wants a custom hook.
+                cgThings.append(CGClassFinalizeHook(descriptor))
+
+                # Only generate a trace hook if the class wants a custom hook.
+                if (descriptor.customTrace):
+                    cgThings.append(CGClassTraceHook(descriptor))
 
         if descriptor.interface.hasInterfaceObject():
             cgThings.append(CGClassConstructHook(descriptor))
             cgThings.append(CGClassHasInstanceHook(descriptor))
             cgThings.append(CGInterfaceObjectJSClass(descriptor))
 
         if descriptor.interface.hasInterfacePrototypeObject():
             cgThings.append(CGPrototypeJSClass(descriptor))
@@ -5603,17 +5683,18 @@ class CGBindingRoot(CGThing):
                       "\n")
 
         # Add header includes.
         curr = CGHeaders(descriptors,
                          dictionaries,
                          ['mozilla/dom/BindingUtils.h',
                           'mozilla/dom/DOMJSClass.h',
                           'mozilla/dom/DOMJSProxyHandler.h'],
-                         ['mozilla/dom/Nullable.h',
+                         ['mozilla/dom/NonRefcountedDOMObject.h',
+                          'mozilla/dom/Nullable.h',
                           'PrimitiveConversions.h',
                           'XPCQuickStubs.h',
                           'nsDOMQS.h',
                           'AccessCheck.h',
                           'WorkerPrivate.h',
                           'nsContentUtils.h',
                           'mozilla/Preferences.h',
                           # Have to include nsDOMQS.h to get fast arg unwrapping
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -1,12 +1,14 @@
 # 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/.
 
+from WebIDL import IDLInterface, IDLExternalInterface
+
 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
 
 class Configuration:
     """
     Represents global configuration state based on IDL parse data and
     the configuration file.
     """
     def __init__(self, filename, parseData):
@@ -18,24 +20,41 @@ class Configuration:
 
         # Build descriptors for all the interfaces we have in the parse data.
         # This allows callers to specify a subset of interfaces by filtering
         # |parseData|.
         self.descriptors = []
         self.interfaces = {}
         self.maxProtoChainLength = 0;
         for thing in parseData:
-            if not thing.isInterface(): continue
+            # Some toplevel things are sadly types, and those have an
+            # isInterface that doesn't mean the same thing as IDLObject's
+            # isInterface()...
+            if (not isinstance(thing, IDLInterface) and
+                not isinstance(thing, IDLExternalInterface)):
+                continue
             iface = thing
-            if iface.identifier.name not in config: continue
             self.interfaces[iface.identifier.name] = iface
-            entry = config[iface.identifier.name]
+            if iface.identifier.name not in config:
+                # Completely skip consequential interfaces with no descriptor
+                # because chances are we don't need to do anything interesting
+                # with them.
+                if iface.isConsequential():
+                    continue
+                entry = {}
+            else:
+                entry = config[iface.identifier.name]
             if not isinstance(entry, list):
                 assert isinstance(entry, dict)
                 entry = [entry]
+            elif len(entry) == 1 and entry[0].get("workers", False):
+                # List with only a workers descriptor means we should
+                # infer a mainthread descriptor.  If you want only
+                # workers bindings, don't use a list here.
+                entry.append({})
             self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
 
         # Mark the descriptors for which only a single nativeType implements
         # an interface.
         for descriptor in self.descriptors:
             intefaceName = descriptor.interface.identifier.name
             otherDescriptors = [d for d in self.descriptors
                                 if d.interface.identifier.name == intefaceName]
@@ -141,18 +160,21 @@ class Descriptor(DescriptorProvider):
 
         self.nativeType = desc.get('nativeType', nativeTypeDefault)
         self.hasInstanceInterface = desc.get('hasInstanceInterface', None)
 
         # Do something sane for JSObject
         if self.nativeType == "JSObject":
             headerDefault = "jsapi.h"
         else:
-            headerDefault = self.nativeType
-            headerDefault = headerDefault.replace("::", "/") + ".h"
+            if self.workers:
+                headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
+            else:
+                headerDefault = self.nativeType
+                headerDefault = headerDefault.replace("::", "/") + ".h"
         self.headerFile = desc.get('headerFile', headerDefault)
 
         if self.interface.isCallback() or self.interface.isExternal():
             if 'castable' in desc:
                 raise TypeError("%s is external or callback but has a castable "
                                 "setting" % self.interface.identifier.name)
             self.castable = False
         else:
@@ -222,17 +244,28 @@ class Descriptor(DescriptorProvider):
                     iface.setUserData('hasProxyDescendant', True)
                     iface = iface.parent
 
         if self.interface.isExternal() and 'prefable' in desc:
             raise TypeError("%s is external but has a prefable setting" %
                             self.interface.identifier.name)
         self.prefable = desc.get('prefable', False)
 
-        self.nativeIsISupports = not self.workers
+        if self.workers:
+            if desc.get('nativeOwnership', 'worker') != 'worker':
+                raise TypeError("Worker descriptor for %s should have 'worker' "
+                                "as value for nativeOwnership" %
+                                self.interface.identifier.name)
+            self.nativeOwnership = "worker"
+        else:
+            self.nativeOwnership = desc.get('nativeOwnership', 'nsisupports')
+            if not self.nativeOwnership in ['owned', 'refcounted', 'nsisupports']:
+                raise TypeError("Descriptor for %s has unrecognized value (%s) "
+                                "for nativeOwnership" %
+                                (self.interface.identifier.name, self.nativeOwnership))
         self.customTrace = desc.get('customTrace', self.workers)
         self.customFinalize = desc.get('customFinalize', self.workers)
         self.wrapperCache = self.workers or desc.get('wrapperCache', True)
 
         if not self.wrapperCache and self.prefable:
             raise TypeError("Descriptor for %s is prefable but not wrappercached" %
                             self.interface.identifier.name)
 
--- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py
+++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
@@ -5,17 +5,17 @@
 import sys
 import string
 
 propList = eval(sys.stdin.read())
 props = ""
 for [prop, pref] in propList:
     extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
     if pref is not "":
-        extendedAttrs.append("Pref=%s" % pref)
+        extendedAttrs.append('Pref="%s"' % pref)
     if not prop.startswith("Moz"):
         prop = prop[0].lower() + prop[1:]
     # Unfortunately, even some of the getters here are fallible
     # (e.g. on nsComputedDOMStyle).
     props += "  [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
                                                    prop)
 
 idlFile = open(sys.argv[1], "r");
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -53,16 +53,17 @@ EXPORTS_mozilla = \
   ErrorResult.h \
   $(NULL)
 
 EXPORTS_$(binding_include_path) = \
   BindingUtils.h \
   DOMJSClass.h \
   DOMJSProxyHandler.h \
   Errors.msg \
+  NonRefcountedDOMObject.h \
   Nullable.h \
   PrimitiveConversions.h \
   PrototypeList.h \
   RegisterBindings.h \
   TypedArray.h \
   UnionConversions.h \
   UnionTypes.h \
   $(exported_binding_headers) \
new file mode 100644
--- /dev/null
+++ b/dom/bindings/NonRefcountedDOMObject.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* 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/. */
+
+#ifndef mozilla_dom_NonRefcountedDOMObject_h__
+#define mozilla_dom_NonRefcountedDOMObject_h__
+
+#include "nsTraceRefcnt.h"
+
+namespace mozilla {
+namespace dom {
+
+// Natives for DOM classes with 'owned' as the value for nativeOwnership in
+// Bindings.conf need to inherit from this class.
+// If you're seeing objects of this class leak then natives for one of the DOM
+// classes with 'owned' as the value for nativeOwnership in Bindings.conf is
+// leaking. If the native for that class has MOZ_COUNT_CTOR/DTOR in its
+// constructor/destructor then it should show up in the leak log too.
+class NonRefcountedDOMObject
+{
+protected:
+  NonRefcountedDOMObject()
+  {
+    MOZ_COUNT_CTOR(NonRefcountedDOMObject);
+  }
+  ~NonRefcountedDOMObject()
+  {
+    MOZ_COUNT_DTOR(NonRefcountedDOMObject);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_NonRefcountedDOMObject_h__ */
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1428,17 +1428,19 @@ class IDLTypedefType(IDLType, IDLObjectW
     def complete(self, parentScope):
         if not self.inner.isComplete():
             self.inner = self.inner.complete(parentScope)
         assert self.inner.isComplete()
         return self.inner
 
     def finish(self, parentScope):
         # Maybe the IDLObjectWithIdentifier for the typedef should be
-        # a separate thing from the type?
+        # a separate thing from the type?  If that happens, we can
+        # remove some hackery around avoiding isInterface() in
+        # Configuration.py.
         self.complete(parentScope)
 
     def validate(self):
         pass
 
     # Do we need a resolveType impl?  I don't think it's particularly useful....
 
     def tag(self):
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -272,47 +272,41 @@ BluetoothAdapter::Notify(const Bluetooth
   if (aData.name().EqualsLiteral("DeviceFound")) {
     nsRefPtr<BluetoothDevice> device = BluetoothDevice::Create(GetOwner(), mPath, aData.value());
     nsCOMPtr<nsIDOMEvent> event;
     NS_NewDOMBluetoothDeviceEvent(getter_AddRefs(event), nullptr, nullptr);
 
     nsCOMPtr<nsIDOMBluetoothDeviceEvent> e = do_QueryInterface(event);
     e->InitBluetoothDeviceEvent(NS_LITERAL_STRING("devicefound"),
                                 false, false, device);
-    e->SetTrusted(true);
-    bool dummy;
-    DispatchEvent(event, &dummy);
+    DispatchTrustedEvent(event);
   } else if (aData.name().EqualsLiteral("DeviceDisappeared")) {
     const nsAString& deviceAddress = aData.value().get_nsString();
 
     nsCOMPtr<nsIDOMEvent> event;
     NS_NewDOMBluetoothDeviceAddressEvent(getter_AddRefs(event), nullptr, nullptr);
 
     nsCOMPtr<nsIDOMBluetoothDeviceAddressEvent> e = do_QueryInterface(event);
     e->InitBluetoothDeviceAddressEvent(NS_LITERAL_STRING("devicedisappeared"),
                                        false, false, deviceAddress);
-    e->SetTrusted(true);
-    bool dummy;
-    DispatchEvent(event, &dummy);
+    DispatchTrustedEvent(e);
   } else if (aData.name().EqualsLiteral("DeviceCreated")) {
     NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue,
                  "DeviceCreated: Invalid value type");
 
     nsRefPtr<BluetoothDevice> device = BluetoothDevice::Create(GetOwner(),
                                                                GetPath(),
                                                                aData.value());
     nsCOMPtr<nsIDOMEvent> event;
     NS_NewDOMBluetoothDeviceEvent(getter_AddRefs(event), nullptr, nullptr);
 
     nsCOMPtr<nsIDOMBluetoothDeviceEvent> e = do_QueryInterface(event);
     e->InitBluetoothDeviceEvent(NS_LITERAL_STRING("devicecreated"),
                                 false, false, device);
-    e->SetTrusted(true);
-    bool dummy;
-    DispatchEvent(event, &dummy);
+    DispatchTrustedEvent(e);
   } else if (aData.name().EqualsLiteral("PropertyChanged")) {
     NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue,
                  "PropertyChanged: Invalid value type");
     arr = aData.value().get_ArrayOfBluetoothNamedValue();
 
     NS_ASSERTION(arr.Length() == 1, "Got more than one property in a change message!");
     BluetoothNamedValue v = arr[0];
 
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -195,31 +195,18 @@ BluetoothDevice::Notify(const BluetoothS
     InfallibleTArray<BluetoothNamedValue> arr = aData.value().get_ArrayOfBluetoothNamedValue();
 
     NS_ASSERTION(arr.Length() == 1, "Got more than one property in a change message!");
     BluetoothNamedValue v = arr[0];
     nsString name = v.name();
 
     SetPropertyByValue(v);
     if (name.EqualsLiteral("Connected")) {
-      nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-      nsresult rv;
-      if (mConnected) {
-        rv = event->InitEvent(NS_LITERAL_STRING("connected"), false, false);
-      } else {
-        rv = event->InitEvent(NS_LITERAL_STRING("disconnected"), false, false);
-      }
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to init the connected/disconnected event!!!");
-        return;
-      }
-
-      event->SetTrusted(true);
-      bool dummy;
-      DispatchEvent(event, &dummy);
+      DispatchTrustedEvent(mConnected ? NS_LITERAL_STRING("connected")
+                           : NS_LITERAL_STRING("disconnected"));
     } else {
       nsRefPtr<BluetoothPropertyEvent> e = BluetoothPropertyEvent::Create(name);
       e->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("propertychanged"));
     }
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device signal: ");
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -14,16 +14,18 @@
 #include "BluetoothServiceUuid.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsIRadioInterfaceLayer.h"
 #include "nsISystemMessagesInternal.h"
+#include "BluetoothUtils.h"
+
 #include "nsVariant.h"
 
 #include <unistd.h> /* usleep() */
 
 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
 #define AUDIO_VOLUME_MASTER "audio.volume.master"
 
 USING_BLUETOOTH_NAMESPACE
@@ -139,58 +141,87 @@ BluetoothHfpManager::Get()
   if (sInstance == nullptr) {
     sInstance = new BluetoothHfpManager();
   }
 
   return sInstance;
 }
 
 bool
-BluetoothHfpManager::BroadcastSystemMessage(const char* aCommand,
-                                            const int aCommandLength)
+BluetoothHfpManager::BroadcastSystemMessage(const nsAString& aType,
+                                            const InfallibleTArray<BluetoothNamedValue>& aData)
 {
-  nsString type;
-  type.AssignLiteral("bluetooth-dialer-command");
-
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   NS_ASSERTION(!::JS_IsExceptionPending(cx),
                "Shouldn't get here when an exception is pending!");
 
   JSAutoRequest jsar(cx);
   JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
   if (!obj) {
     NS_WARNING("Failed to new JSObject for system message!");
     return false;
   }
 
-  JSString* JsData = JS_NewStringCopyN(cx, aCommand, aCommandLength);
-  if (!JsData) {
-    NS_WARNING("JS_NewStringCopyN is out of memory");
-    return false;
-  }
-
-  jsval v = STRING_TO_JSVAL(JsData);
-  if (!JS_SetProperty(cx, obj, "command", &v)) {
+  if (!SetJsObject(cx, obj, aData)) {
     NS_WARNING("Failed to set properties of system message!");
     return false;
   }
 
   nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
     do_GetService("@mozilla.org/system-message-internal;1");
 
   if (!systemMessenger) {
     NS_WARNING("Failed to get SystemMessenger service!");
     return false;
   }
 
-  systemMessenger->BroadcastMessage(type, OBJECT_TO_JSVAL(obj));
+  systemMessenger->BroadcastMessage(aType, OBJECT_TO_JSVAL(obj));
 
   return true;
 }
 
+void
+BluetoothHfpManager::NotifySettings(const bool aConnected)
+{
+  nsString type, name;
+  BluetoothValue v;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-hfp-status-changed");
+
+  name.AssignLiteral("connected");
+  v = aConnected;
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  name.AssignLiteral("address");
+  v = GetAddressFromObjectPath(mDevicePath);
+  parameters.AppendElement(BluetoothNamedValue(name, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    NS_WARNING("Failed to broadcast system message to dialer");
+    return;
+  }
+}
+
+void
+BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
+{
+  nsString type, name, command;
+  command = aCommand;
+  InfallibleTArray<BluetoothNamedValue> parameters;
+  type.AssignLiteral("bluetooth-dialer-command");
+
+  BluetoothValue v(command);
+  parameters.AppendElement(BluetoothNamedValue(type, v));
+
+  if (!BroadcastSystemMessage(type, parameters)) {
+    NS_WARNING("Failed to broadcast system message to dialer");
+    return;
+  }
+}
+
 nsresult
 BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The string that we're interested in will be a JSON string that looks like:
   //  {"key":"volumeup", "value":1.0}
   //  {"key":"volumedown", "value":0.2}
@@ -310,16 +341,18 @@ BluetoothHfpManager::ReceiveSocketData(U
     // FIXME - Bug 794349
     // This value reflects current status of telephony, roaming, battery ...,
     // so obviously fixed value must be wrong if there is an ongoing call. 
     // Need a patch for this, but currently just using fixed value for basic 
     // SLC establishment.
     SendLine("+CIND: 5,5,1,0,0,0,0");
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CMER=", 8)) {
+    // SLC establishment
+    NotifySettings(true);
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHLD=?", 9)) {
     SendLine("+CHLD: (0,1,2,3)");
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHLD=", 8)) {
     SendLine("OK");
   } else if (!strncmp(msg, "AT+VGS=", 7)) {
     // VGS range: [0, 15]
@@ -344,32 +377,23 @@ BluetoothHfpManager::ReceiveSocketData(U
     } else if (newVgs < mCurrentVgs) {
       os->NotifyObservers(nullptr, "bluetooth-volume-change", NS_LITERAL_STRING("down").get());
     }
 
     mCurrentVgs = newVgs;
 
     SendLine("OK");
   } else if (!strncmp(msg, "AT+BLDN", 7)) {
-    if (!BroadcastSystemMessage("BLDN", 4)) {
-      NS_WARNING("Failed to broadcast system message to dialer");
-      return;
-    }
+    NotifyDialer(NS_LITERAL_STRING("BLDN"));
     SendLine("OK");
   } else if (!strncmp(msg, "ATA", 3)) {
-    if (!BroadcastSystemMessage("ATA", 3)) {
-      NS_WARNING("Failed to broadcast system message to dialer");
-      return;
-    }
+    NotifyDialer(NS_LITERAL_STRING("ATA"));
     SendLine("OK");
   } else if (!strncmp(msg, "AT+CHUP", 7)) {
-    if (!BroadcastSystemMessage("CHUP", 4)) {
-      NS_WARNING("Failed to broadcast system message to dialer");
-      return;
-    }
+    NotifyDialer(NS_LITERAL_STRING("CHUP"));
     SendLine("OK");
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling HFP message, reply ok: ");
     warningMsg.Append(msg);
     NS_WARNING(warningMsg.get());
 #endif
@@ -402,19 +426,39 @@ BluetoothHfpManager::Connect(const nsASt
                                         false,
                                         this,
                                         runnable);
 
   runnable.forget();
   return NS_FAILED(rv) ? false : true;
 }
 
+bool
+BluetoothHfpManager::Listen()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return false;
+  }
+
+  nsresult rv = bs->ListenSocketViaService(BluetoothReservedChannels::HANDSFREE_AG,
+                                           BluetoothSocketType::RFCOMM,
+                                           true,
+                                           false,
+                                           this);
+  return NS_FAILED(rv) ? false : true;
+}
+
 void
 BluetoothHfpManager::Disconnect()
 {
+  NotifySettings(false);
   CloseSocket();
 }
 
 bool
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
   const char* kHfpCrlf = "\xd\xa";
   nsAutoCString msg;
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -10,16 +10,17 @@
 #include "BluetoothCommon.h"
 #include "BluetoothRilListener.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "nsIObserver.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
+class BluetoothNamedValue;
 
 class BluetoothHfpManager : public mozilla::ipc::UnixSocketConsumer
                           , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
@@ -29,23 +30,25 @@ public:
   void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage);
 
   bool Connect(const nsAString& aDeviceObjectPath,
                BluetoothReplyRunnable* aRunnable);
   void Disconnect();
   bool SendLine(const char* aMessage);
   void CallStateChanged(int aCallIndex, int aCallState,
                         const char* aNumber, bool aIsActive);
-
+  bool Listen();
 private:
   BluetoothHfpManager();
 
-  bool BroadcastSystemMessage(const char* aCommand,
-                              const int aCommandLength);
   nsresult HandleVolumeChanged(const nsAString& aData);
+  bool BroadcastSystemMessage(const nsAString& aType,
+                              const InfallibleTArray<BluetoothNamedValue>& aData);
+  void NotifyDialer(const nsAString& aCommand);
+  void NotifySettings(const bool aConnected);
 
   int mCurrentVgs;
   int mCurrentCallIndex;
   int mCurrentCallState;
   nsAutoPtr<BluetoothRilListener> mListener;
   nsString mDevicePath;
 };
 
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -122,36 +122,18 @@ public:
 private:
   nsRefPtr<BluetoothManager> mManagerPtr;
   bool mEnabled;
 };
 
 nsresult
 BluetoothManager::FireEnabledDisabledEvent(bool aEnabled)
 {
-  nsString eventName;
-
-  if (aEnabled) {
-    eventName.AssignLiteral("enabled");
-  } else {
-    eventName.AssignLiteral("disabled");
-  }
-
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(eventName, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return DispatchTrustedEvent(aEnabled ? NS_LITERAL_STRING("enabled")
+                              : NS_LITERAL_STRING("disabled"));
 }
 
 BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow)
 : BluetoothPropertyContainer(BluetoothObjectType::TYPE_MANAGER)
 {
   MOZ_ASSERT(aWindow);
 
   BindToOwner(aWindow);
@@ -268,27 +250,17 @@ NS_NewBluetoothManager(nsPIDOMWindow* aW
   bluetoothManager.forget(aBluetoothManager);
   return NS_OK;
 }
 
 void
 BluetoothManager::Notify(const BluetoothSignal& aData)
 {
   if (aData.name().EqualsLiteral("AdapterAdded")) {
-    nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-    nsresult rv = event->InitEvent(NS_LITERAL_STRING("adapteradded"), false, false);
-
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to init the adapteradded event!!!");
-      return;
-    }
-
-    event->SetTrusted(true);
-    bool dummy;
-    DispatchEvent(event, &dummy);
+    DispatchTrustedEvent(NS_LITERAL_STRING("adapteradded"));
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling manager signal: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
     NS_WARNING(warningMsg.get());
 #endif
   }
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -20,41 +20,41 @@ public:
   NS_DECL_NSIRILTELEPHONYCALLBACK
 
   BluetoothRILTelephonyCallback() { }
 };
 
 NS_IMPL_ISUPPORTS1(BluetoothRILTelephonyCallback, nsIRILTelephonyCallback)
 
 NS_IMETHODIMP
-BluetoothRILTelephonyCallback::CallStateChanged(PRUint32 aCallIndex,
-                                                PRUint16 aCallState,
+BluetoothRILTelephonyCallback::CallStateChanged(uint32_t aCallIndex,
+                                                uint16_t aCallState,
                                                 const nsAString& aNumber,
                                                 bool aIsActive)
 {
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   hfp->CallStateChanged(aCallIndex, aCallState,
                         NS_ConvertUTF16toUTF8(aNumber).get(), aIsActive);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-BluetoothRILTelephonyCallback::EnumerateCallState(PRUint32 aCallIndex,
-                                                  PRUint16 aCallState,
+BluetoothRILTelephonyCallback::EnumerateCallState(uint32_t aCallIndex,
+                                                  uint16_t aCallState,
                                                   const nsAString_internal& aNumber,
                                                   bool aIsActive,
                                                   bool* aResult)
 {
   *aResult = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-BluetoothRILTelephonyCallback::NotifyError(PRInt32 aCallIndex,
+BluetoothRILTelephonyCallback::NotifyError(int32_t aCallIndex,
                                            const nsAString& aError)
 {
   return NS_OK;
 }
 
 BluetoothRilListener::BluetoothRilListener()
 {
   mRILTelephonyCallback = new BluetoothRILTelephonyCallback();
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -281,16 +281,23 @@ public:
   DisconnectObjectPush(BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual bool
   SendFile(const nsAString& aDeviceAddress,
            BlobParent* aBlobParent,
            BlobChild* aBlobChild,
            BluetoothReplyRunnable* aRunnable) = 0;
 
+  virtual nsresult
+  ListenSocketViaService(int aChannel,
+                         BluetoothSocketType aType,
+                         bool aAuth,
+                         bool aEncrypt,
+                         mozilla::ipc::UnixSocketConsumer* aConsumer) = 0;
+
   bool
   IsEnabled() const
   {
     return mEnabled;
   }
 
 protected:
   BluetoothService()
--- a/dom/bluetooth/BluetoothServiceUuid.h
+++ b/dom/bluetooth/BluetoothServiceUuid.h
@@ -25,14 +25,29 @@ namespace BluetoothServiceUuid {
 namespace BluetoothServiceUuidStr {
   static const char* Headset       = "00001108-0000-1000-8000-00805F9B34FB";
   static const char* HeadsetAG     = "00001112-0000-1000-8000-00805F9B34FB";
   static const char* Handsfree     = "0000111E-0000-1000-8000-00805F9B34FB";
   static const char* HandsfreeAG   = "0000111F-0000-1000-8000-00805F9B34FB";
   static const char* ObjectPush    = "00001105-0000-1000-8000-00805F9B34FB";
 }
 
+// TODO/qdot: Move these back into gonk and make the service handler deal with
+// it there.
+//
+// Gotten from reading the "u8" values in B2G/external/bluez/src/adapter.c
+// These were hardcoded into android
+enum BluetoothReservedChannels {
+  DIALUP_NETWORK = 1,
+  HANDSFREE_AG = 10,
+  HEADSET_AG = 11,
+  OPUSH = 12,
+  SIM_ACCESS = 15,
+  PBAP_PSE = 19,
+  FTP = 20,
+};
+
 }
 }
 }
 
 #endif
 
--- a/dom/bluetooth/BluetoothUnixSocketConnector.cpp
+++ b/dom/bluetooth/BluetoothUnixSocketConnector.cpp
@@ -30,16 +30,25 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sco.h>
 #include <bluetooth/rfcomm.h>
 #include <bluetooth/l2cap.h>
 
 #include "BluetoothUnixSocketConnector.h"
 #include "nsThreadUtils.h"
 
+#undef LOG
+#if defined(MOZ_WIDGET_GONK)
+#include <android/log.h>
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
+#else
+#define BTDEBUG true
+#define LOG(args...) if (BTDEBUG) printf(args);
+#endif
+
 USING_BLUETOOTH_NAMESPACE
 
 static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
 
 static
 int get_bdaddr(const char *str, bdaddr_t *ba)
 {
   char *d = ((char*)ba) + 5, *endp;
@@ -57,119 +66,116 @@ BluetoothUnixSocketConnector::BluetoothU
   bool aAuth,
   bool aEncrypt) : mType(aType)
                  , mChannel(aChannel)
                  , mAuth(aAuth)
                  , mEncrypt(aEncrypt)
 {
 }
 
-int
-BluetoothUnixSocketConnector::Create()
+bool
+BluetoothUnixSocketConnector::Setup(int aFd)
 {
-  MOZ_ASSERT(!NS_IsMainThread());
   int lm = 0;
-  int fd = -1;
   int sndbuf;
-
-  switch (mType) {
-  case BluetoothSocketType::RFCOMM:
-    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-    break;
-  case BluetoothSocketType::SCO:
-    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-    break;
-  case BluetoothSocketType::L2CAP:
-    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-    break;
-  default:
-    return -1;
-  }
-
-  if (fd < 0) {
-    NS_WARNING("Could not open bluetooth socket!");
-    return -1;
-  }
-
   /* kernel does not yet support LM for SCO */
   switch (mType) {
   case BluetoothSocketType::RFCOMM:
     lm |= mAuth ? RFCOMM_LM_AUTH : 0;
     lm |= mEncrypt ? RFCOMM_LM_ENCRYPT : 0;
     lm |= (mAuth && mEncrypt) ? RFCOMM_LM_SECURE : 0;
     break;
   case BluetoothSocketType::L2CAP:
     lm |= mAuth ? L2CAP_LM_AUTH : 0;
     lm |= mEncrypt ? L2CAP_LM_ENCRYPT : 0;
     lm |= (mAuth && mEncrypt) ? L2CAP_LM_SECURE : 0;
     break;
   }
 
   if (lm) {
-    if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
+    if (setsockopt(aFd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
       NS_WARNING("setsockopt(RFCOMM_LM) failed, throwing");
-      return -1;
+      return false;
     }
   }
 
   if (mType == BluetoothSocketType::RFCOMM) {
     sndbuf = RFCOMM_SO_SNDBUF;
-    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
+    if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
       NS_WARNING("setsockopt(SO_SNDBUF) failed, throwing");
-      return -1;
+      return false;
     }
   }
 
+  return true;
+}
+
+int
+BluetoothUnixSocketConnector::Create()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  int fd = -1;
+
+  switch (mType) {
+  case BluetoothSocketType::RFCOMM:
+    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+    break;
+  case BluetoothSocketType::SCO:
+    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+    break;
+  case BluetoothSocketType::L2CAP:
+    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+    break;
+  default:
+    MOZ_NOT_REACHED();
+  }
+
+  if (fd < 0) {
+    NS_WARNING("Could not open bluetooth socket!");
+    return -1;
+  }
+
+  if (!Setup(fd)) {
+    NS_WARNING("Could not set up socket!");
+  }
   return fd;
 }
 
-bool
-BluetoothUnixSocketConnector::ConnectInternal(int aFd, const char* aAddress)
+void
+BluetoothUnixSocketConnector::CreateAddr(bool aIsServer,
+                                         socklen_t& aAddrSize,
+                                         struct sockaddr* aAddr,
+                                         const char* aAddress)
 {
-  int n = 1;
-  setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
-  
-  socklen_t addr_sz;
-  struct sockaddr *addr;
-  bdaddr_t bd_address_obj;
+  // Set to BDADDR_ANY, if it's not a server, we'll reset.
+  bdaddr_t bd_address_obj = {{0, 0, 0, 0, 0, 0}};
 
-  if (get_bdaddr(aAddress, &bd_address_obj)) {
-    NS_WARNING("Can't get bluetooth address!");
-    return false;
+  if (!aIsServer && aAddress && strlen(aAddress) > 0) {
+    if (get_bdaddr(aAddress, &bd_address_obj)) {
+      NS_WARNING("Can't get bluetooth address!");
+      return;
+    }
   }
 
   switch (mType) {
   case BluetoothSocketType::RFCOMM:
     struct sockaddr_rc addr_rc;
-    addr = (struct sockaddr *)&addr_rc;
-    addr_sz = sizeof(addr_rc);
-
-    memset(addr, 0, addr_sz);
+    aAddrSize = sizeof(addr_rc);
+    memset(aAddr, 0, aAddrSize);
     addr_rc.rc_family = AF_BLUETOOTH;
     addr_rc.rc_channel = mChannel;
     memcpy(&addr_rc.rc_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
+    memcpy(aAddr, &addr_rc, sizeof(addr_rc));
     break;
   case BluetoothSocketType::SCO:
     struct sockaddr_sco addr_sco;
-    addr = (struct sockaddr *)&addr_sco;
-    addr_sz = sizeof(addr_sco);
+    aAddrSize = sizeof(addr_sco);
 
-    memset(addr, 0, addr_sz);
+    memset(aAddr, 0, aAddrSize);
     addr_sco.sco_family = AF_BLUETOOTH;
     memcpy(&addr_sco.sco_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
+    memcpy(aAddr, &addr_sco, sizeof(addr_sco));
     break;
   default:
     NS_WARNING("Socket type unknown!");
-    return false;
   }
-
-  int ret = connect(aFd, addr, addr_sz);
+}
 
-  if (ret) {
-#if DEBUG
-    //LOG("Socket connect errno=%d\n", errno);
-#endif
-    NS_WARNING("Socket connect error!");
-    return false;
-  }
-
-  return true;
-}
--- a/dom/bluetooth/BluetoothUnixSocketConnector.h
+++ b/dom/bluetooth/BluetoothUnixSocketConnector.h
@@ -3,30 +3,34 @@
 /* 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/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
 #define mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
 
 #include "BluetoothCommon.h"
+#include <sys/socket.h>
 #include <mozilla/ipc/UnixSocket.h>
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothUnixSocketConnector : public mozilla::ipc::UnixSocketConnector
 {
 public:
   BluetoothUnixSocketConnector(BluetoothSocketType aType, int aChannel,
                                bool aAuth, bool aEncrypt);
   virtual ~BluetoothUnixSocketConnector()
   {}
   virtual int Create() MOZ_OVERRIDE;
-  virtual bool ConnectInternal(int aFd, const char* aAddress) MOZ_OVERRIDE;
-
+  virtual void CreateAddr(bool aIsServer,
+                          socklen_t& aAddrSize,
+                          struct sockaddr* aAddr,
+                          const char* aAddress) MOZ_OVERRIDE;
+  virtual bool Setup(int aFd) MOZ_OVERRIDE;
 private:
   BluetoothSocketType mType;
   int mChannel;
   bool mAuth;
   bool mEncrypt;
 };
 
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/BluetoothUtils.cpp
+++ b/dom/bluetooth/BluetoothUtils.cpp
@@ -1,21 +1,24 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
+#include "base/basictypes.h"
+
 #include "BluetoothUtils.h"
 #include "nsContentUtils.h"
 #include "BluetoothDevice.h"
 #include "jsapi.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "mozilla/Scoped.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 nsresult
 mozilla::dom::bluetooth::StringArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
                                               const nsTArray<nsString>& aSourceArray,
                                               JSObject** aResultArray)
 {
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aGlobal, "Null global!");
@@ -93,8 +96,73 @@ mozilla::dom::bluetooth::BluetoothDevice
 
   if (!JS_FreezeObject(aCx, arrayObj)) {
     return NS_ERROR_FAILURE;
   }
 
   *aResultArray = arrayObj;
   return NS_OK;
 }
+
+bool
+mozilla::dom::bluetooth::SetJsObject(JSContext* aContext,
+                                     JSObject* aObj,
+                                     const InfallibleTArray<BluetoothNamedValue>& aData)
+{
+  for (uint32_t i = 0; i < aData.Length(); i++) {
+    jsval v;
+    if (aData[i].value().type() == BluetoothValue::TnsString) {
+      nsString data = aData[i].value().get_nsString();
+      JSString* JsData = JS_NewStringCopyN(aContext,
+                                           NS_ConvertUTF16toUTF8(data).get(),
+                                           data.Length());
+      NS_ENSURE_TRUE(JsData, NS_ERROR_OUT_OF_MEMORY);
+      v = STRING_TO_JSVAL(JsData);
+    } else if (aData[i].value().type() == BluetoothValue::Tuint32_t) {
+      int data = aData[i].value().get_uint32_t();
+      v = INT_TO_JSVAL(data);
+    } else if (aData[i].value().type() == BluetoothValue::Tbool) {
+      bool data = aData[i].value().get_bool();
+      v = BOOLEAN_TO_JSVAL(data);
+    } else {
+      NS_WARNING("SetJsObject: Parameter is not handled");
+    }
+
+    if (!JS_SetProperty(aContext, aObj,
+                        NS_ConvertUTF16toUTF8(aData[i].name()).get(),
+                        &v)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+nsString
+mozilla::dom::bluetooth::GetObjectPathFromAddress(const nsAString& aAdapterPath,
+                                                  const nsAString& aDeviceAddress)
+{
+  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
+  // and the adapter path would be the first part of the object path, according
+  // to the example above, it's /org/bluez/2906/hci0.
+  nsString devicePath(aAdapterPath);
+  devicePath.AppendLiteral("/dev_");
+  devicePath.Append(aDeviceAddress);
+  devicePath.ReplaceChar(':', '_');
+  return devicePath;
+}
+
+nsString
+mozilla::dom::bluetooth::GetAddressFromObjectPath(const nsAString& aObjectPath)
+{
+  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
+  // and the adapter path would be the first part of the object path, according
+  // to the example above, it's /org/bluez/2906/hci0.
+  nsString address(aObjectPath);
+  int addressHead = address.RFind("/") + 5;
+
+  MOZ_ASSERT(addressHead + BLUETOOTH_ADDRESS_LENGTH == address.Length());
+
+  address.Cut(0, addressHead);
+  address.ReplaceChar('_', ':');
+
+  return address;
+}
+
--- a/dom/bluetooth/BluetoothUtils.h
+++ b/dom/bluetooth/BluetoothUtils.h
@@ -10,22 +10,35 @@
 #include "BluetoothCommon.h"
 
 class JSContext;
 class JSObject;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothDevice;
+class BluetoothNamedValue;
 
 nsresult
 StringArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
                      const nsTArray<nsString>& aSourceArray,
                      JSObject** aResultArray);
 
 nsresult
 BluetoothDeviceArrayToJSArray(JSContext* aCx, JSObject* aGlobal,
                               const nsTArray<nsRefPtr<BluetoothDevice> >& aSourceArray,
                               JSObject** aResultArray);
 
+bool
+SetJsObject(JSContext* aContext,
+            JSObject* aObj,
+            const InfallibleTArray<BluetoothNamedValue>& aData);
+
+nsString
+GetObjectPathFromAddress(const nsAString& aAdapterPath,
+                         const nsAString& aDeviceAddress);
+
+nsString
+GetAddressFromObjectPath(const nsAString& aObjectPath);
+
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -221,16 +221,29 @@ BluetoothServiceChildProcess::GetSocketV
                                        bool aEncrypt,
                                        mozilla::ipc::UnixSocketConsumer* aConsumer,
                                        BluetoothReplyRunnable* aRunnable)
 {
   MOZ_NOT_REACHED("This should never be called!");
   return NS_ERROR_FAILURE;
 }
 
+
+nsresult
+BluetoothServiceChildProcess::ListenSocketViaService(
+  int aChannel,
+  BluetoothSocketType aType,
+  bool aAuth,
+  bool aEncrypt,
+  mozilla::ipc::UnixSocketConsumer* aConsumer)
+{
+  MOZ_NOT_REACHED("This should never be called!");
+  return NS_ERROR_FAILURE;
+}
+
 bool
 BluetoothServiceChildProcess::SetPinCodeInternal(
                                                 const nsAString& aDeviceAddress,
                                                 const nsAString& aPinCode,
                                                 BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
               SetPinCodeRequest(nsString(aDeviceAddress), nsString(aPinCode)));
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -96,16 +96,23 @@ public:
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
                       BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
                       mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
+  virtual nsresult
+  ListenSocketViaService(int aChannel,
+                         BluetoothSocketType aType,
+                         bool aAuth,
+                         bool aEncrypt,
+                         mozilla::ipc::UnixSocketConsumer* aConsumer) MOZ_OVERRIDE;
+
   virtual bool
   SetPinCodeInternal(const nsAString& aDeviceAddress,
                      const nsAString& aPinCode,
                      BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual bool
   SetPasskeyInternal(const nsAString& aDeviceAddress,
                      uint32_t aPasskey,
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -19,16 +19,17 @@
 #include "base/basictypes.h"
 #include "BluetoothDBusService.h"
 #include "BluetoothHfpManager.h"
 #include "BluetoothOppManager.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothScoManager.h"
 #include "BluetoothServiceUuid.h"
 #include "BluetoothUnixSocketConnector.h"
+#include "BluetoothUtils.h"
 
 #include <cstdio>
 #include <dbus/dbus.h>
 
 #include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "AudioManager.h"
 #include "nsAutoPtr.h"
@@ -149,47 +150,16 @@ static const char* sBluetoothDBusSignals
 static nsAutoPtr<RawDBusConnection> gThreadConnection;
 static nsDataHashtable<nsStringHashKey, DBusMessage* > sPairingReqTable;
 static nsDataHashtable<nsStringHashKey, DBusMessage* > sAuthorizeReqTable;
 static nsString sDefaultAdapterPath;
 static nsTArray<uint32_t> sServiceHandles;
 
 typedef void (*UnpackFunc)(DBusMessage*, DBusError*, BluetoothValue&, nsAString&);
 
-static nsString
-GetObjectPathFromAddress(const nsAString& aAdapterPath,
-                         const nsAString& aDeviceAddress)
-{
-  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
-  // and the adapter path would be the first part of the object path, according
-  // to the example above, it's /org/bluez/2906/hci0.
-  nsString devicePath(aAdapterPath);
-  devicePath.AppendLiteral("/dev_");
-  devicePath.Append(aDeviceAddress);
-  devicePath.ReplaceChar(':', '_');
-  return devicePath;
-}
-
-static nsString
-GetAddressFromObjectPath(const nsAString& aObjectPath)
-{
-  // The object path would be like /org/bluez/2906/hci0/dev_00_23_7F_CB_B4_F1,
-  // and the adapter path would be the first part of the object path, according
-  // to the example above, it's /org/bluez/2906/hci0.
-  nsString address(aObjectPath);
-  int addressHead = address.RFind("/") + 5;
-
-  MOZ_ASSERT(addressHead + BLUETOOTH_ADDRESS_LENGTH == address.Length());
-
-  address.Cut(0, addressHead);
-  address.ReplaceChar('_', ':');
-
-  return address;
-}
-
 class DistributeBluetoothSignalTask : public nsRunnable {
   BluetoothSignal mSignal;
 public:
   DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) :
     mSignal(aSignal)
   {
   }
 
@@ -723,16 +693,39 @@ ExtractHandles(DBusMessage *aReply, nsTA
     }
   } else {
     LOG_AND_FREE_DBUS_ERROR(&err);
   }
 }
 
 // static
 bool
+BluetoothDBusService::AddServiceRecords(const nsAString& aAdapterPath,
+                                        const char* serviceName,
+                                        unsigned long long uuidMsb,
+                                        unsigned long long uuidLsb,
+                                        int channel)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  DBusMessage *reply;
+  reply = dbus_func_args(gThreadConnection->GetConnection(),
+                         NS_ConvertUTF16toUTF8(aAdapterPath).get(),
+                         DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord",
+                         DBUS_TYPE_STRING, &serviceName,
+                         DBUS_TYPE_UINT64, &uuidMsb,
+                         DBUS_TYPE_UINT64, &uuidLsb,
+                         DBUS_TYPE_UINT16, &channel,
+                         DBUS_TYPE_INVALID);
+
+  return reply ? dbus_returns_uint32(reply) : -1;
+}
+
+// static
+bool
 BluetoothDBusService::AddReservedServicesInternal(const nsAString& aAdapterPath,
                                                   const nsTArray<uint32_t>& aServices,
                                                   nsTArray<uint32_t>& aServiceHandlesContainer)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   int length = aServices.Length();
   if (length == 0) return false;
@@ -2272,17 +2265,19 @@ public:
 
   nsresult
   Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsString address = GetAddressFromObjectPath(mObjectPath);
     nsString replyError;
-    BluetoothUnixSocketConnector c(BluetoothSocketType::SCO, -1, mAuth, mEncrypt);
+    BluetoothUnixSocketConnector* c =
+      new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
+                                       mAuth, mEncrypt);
 
     BluetoothScoManager* sco = BluetoothScoManager::Get();
     if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
       replyError.AssignLiteral("SocketConnectionError");
       sco->SetConnected(false); 
       return NS_ERROR_FAILURE;
     }
     sco->SetConnected(true);
@@ -2297,47 +2292,47 @@ public:
 
 private:
   nsRefPtr<UnixSocketConsumer> mConsumer;
   nsString mObjectPath;
   bool mAuth;
   bool mEncrypt;
 };
 
-class CreateBluetoothSocketRunnable : public nsRunnable
+class ConnectBluetoothSocketRunnable : public nsRunnable
 {
 public:
-  CreateBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
-                                UnixSocketConsumer* aConsumer,
-                                const nsAString& aObjectPath,
-                                const nsAString& aServiceUUID,
-                                BluetoothSocketType aType,
-                                bool aAuth,
-                                bool aEncrypt)
+  ConnectBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
+                                 UnixSocketConsumer* aConsumer,
+                                 const nsAString& aObjectPath,
+                                 const nsAString& aServiceUUID,
+                                 BluetoothSocketType aType,
+                                 bool aAuth,
+                                 bool aEncrypt)
     : mRunnable(dont_AddRef(aRunnable)),
       mConsumer(aConsumer),
       mObjectPath(aObjectPath),
       mServiceUUID(aServiceUUID),
       mType(aType),
       mAuth(aAuth),
       mEncrypt(aEncrypt)
   {
   }
 
   nsresult
   Run()
   {
-    NS_WARNING("Running create socket!\n");
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsString address = GetAddressFromObjectPath(mObjectPath);
     int channel = GetDeviceServiceChannel(mObjectPath, mServiceUUID, 0x0004);
     BluetoothValue v;
     nsString replyError;
-    BluetoothUnixSocketConnector c(mType, channel, mAuth, mEncrypt);
+    BluetoothUnixSocketConnector* c =
+      new BluetoothUnixSocketConnector(mType, channel, mAuth, mEncrypt);
     if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
       replyError.AssignLiteral("SocketConnectionError");
       DispatchBluetoothReply(mRunnable, v, replyError);
       return NS_ERROR_FAILURE;
     }
     // Bluetooth value needs to be set to something to succeed.
     v = true;
     DispatchBluetoothReply(mRunnable, v, replyError);
@@ -2390,17 +2385,17 @@ BluetoothDBusService::GetSocketViaServic
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
   if (!mConnection || !gThreadConnection) {
     NS_ERROR("Bluetooth service not started yet!");
     return NS_ERROR_FAILURE;
   }
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
-  nsRefPtr<nsRunnable> func(new CreateBluetoothSocketRunnable(runnable,
+  nsRefPtr<nsRunnable> func(new ConnectBluetoothSocketRunnable(runnable,
                                                               aConsumer,
                                                               aObjectPath,
                                                               aService, aType,
                                                               aAuth, aEncrypt));
   if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Cannot dispatch firmware loading task!");
     return NS_ERROR_FAILURE;
   }
@@ -2419,8 +2414,69 @@ BluetoothDBusService::SendFile(const nsA
   // so we don't need aDeviceAddress here because the target device
   // has been determined when calling 'Connect()'. Nevertheless, keep
   // it for future use.
   BluetoothOppManager* opp = BluetoothOppManager::Get();
   opp->SendFile(aBlobParent, aRunnable);
 
   return true;
 }
+
+class ListenBluetoothSocketRunnable : public nsRunnable
+{
+public:
+  ListenBluetoothSocketRunnable(UnixSocketConsumer* aConsumer,
+                                int aChannel,
+                                BluetoothSocketType aType,
+                                bool aAuth,
+                                bool aEncrypt)
+    : mConsumer(aConsumer)
+    , mChannel(aChannel)
+    , mType(aType)
+    , mAuth(aAuth)
+    , mEncrypt(aEncrypt)
+  {
+  }
+
+  nsresult
+  Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    BluetoothUnixSocketConnector* c =
+      new BluetoothUnixSocketConnector(mType, mChannel, mAuth, mEncrypt);
+    if (!mConsumer->ListenSocket(c)) {
+      NS_WARNING("Can't listen on socket!");
+      return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<UnixSocketConsumer> mConsumer;
+  int mChannel;
+  BluetoothSocketType mType;
+  bool mAuth;
+  bool mEncrypt;
+};
+
+nsresult
+BluetoothDBusService::ListenSocketViaService(int aChannel,
+                                             BluetoothSocketType aType,
+                                             bool aAuth,
+                                             bool aEncrypt,
+                                             mozilla::ipc::UnixSocketConsumer* aConsumer)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
+  if (!mConnection || !gThreadConnection) {
+    NS_ERROR("Bluetooth service not started yet!");
+    return NS_ERROR_FAILURE;
+  }
+  nsRefPtr<nsRunnable> func(new ListenBluetoothSocketRunnable(aConsumer,
+                                                              aChannel, aType,
+                                                              aAuth,
+                                                              aEncrypt));
+  if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Cannot dispatch firmware loading task!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -54,16 +54,30 @@ public:
               BluetoothReplyRunnable* aRunnable);
 
   virtual bool
   GetDevicePath(const nsAString& aAdapterPath,
                 const nsAString& aDeviceAddress,
                 nsAString& aDevicePath);
 
   static bool
+  AddServiceRecords(const nsAString& aAdapterPath,
+                    const char* serviceName,
+                    unsigned long long uuidMsb,
+                    unsigned long long uuidLsb,
+                    int channel);
+
+  static bool
+  RemoveServiceRecords(const nsAString& aAdapterPath,
+                       const char* serviceName,
+                       unsigned long long uuidMsb,
+                       unsigned long long uuidLsb,
+                       int channel);
+
+  static bool
   AddReservedServicesInternal(const nsAString& aAdapterPath,
                               const nsTArray<uint32_t>& aServices,
                               nsTArray<uint32_t>& aServiceHandlesContainer);
 
   static bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<uint32_t>& aServiceHandles);
 
@@ -78,16 +92,23 @@ public:
                       const nsAString& aService,
                       BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
                       mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
+  ListenSocketViaService(int aChannel,
+                         BluetoothSocketType aType,
+                         bool aAuth,
+                         bool aEncrypt,
+                         mozilla::ipc::UnixSocketConsumer* aConsumer);
+
+  virtual nsresult
   CreatePairedDeviceInternal(const nsAString& aAdapterPath,
                              const nsAString& aDeviceAddress,
                              int aTimeout,
                              BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
                        const nsAString& aDeviceObjectPath,
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -153,18 +153,18 @@ nsDOMCameraManager::Shutdown(uint64_t aW
   DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId);
   MOZ_ASSERT(NS_IsMainThread());
 
   CameraControls* controls = sActiveWindows.Get(aWindowId);
   if (!controls) {
     return;
   }
 
-  PRUint32 length = controls->Length();
-  for (PRUint32 i = 0; i < length; i++) {
+  uint32_t length = controls->Length();
+  for (uint32_t i = 0; i < length; i++) {
     nsRefPtr<nsDOMCameraControl> cameraControl = controls->ElementAt(i);
     cameraControl->Shutdown();
   }
   controls->Clear();
 
   sActiveWindows.Remove(aWindowId);
 }
 
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -88,19 +88,19 @@ protected:
   uint32_t                  mFormat;
 
   uint32_t                  mFps;
   uint32_t                  mDiscardedFrameCount;
 
   android::MediaProfiles*   mMediaProfiles;
   android::GonkRecorder*    mRecorder;
 
-  PRUint32                  mVideoRotation;
-  PRUint32                  mVideoWidth;
-  PRUint32                  mVideoHeight;
+  uint32_t                  mVideoRotation;
+  uint32_t                  mVideoWidth;
+  uint32_t                  mVideoHeight;
   nsString                  mVideoFile;
 
   // camcorder profile settings for the desired quality level
   int mDuration;        // max recording duration (ignored)
   int mVideoFileFormat; // output file format
   int mVideoCodec;      // video encoder
   int mVideoBitRate;    // video bit rate
   int mVideoFrameRate;  // video frame rate
--- a/dom/file/FileRequest.cpp
+++ b/dom/file/FileRequest.cpp
@@ -147,19 +147,10 @@ FileRequest::FireProgressEvent(uint64_t 
   nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
   MOZ_ASSERT(progress);
   rv = progress->InitProgressEvent(NS_LITERAL_STRING("progress"), false, false,
                                    false, aLoaded, aTotal);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  rv = event->SetTrusted(true);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  if (NS_FAILED(rv)) {
-    return;
-  }
+  DispatchTrustedEvent(event);
 }
--- a/dom/fm/FMRadio.cpp
+++ b/dom/fm/FMRadio.cpp
@@ -195,47 +195,27 @@ NS_IMETHODIMP FMRadio::SetFrequency(int3
   return NS_OK;
 }
 
 void FMRadio::Notify(const SwitchEvent& aEvent)
 {
   if (mHeadphoneState != aEvent.status()) {
     LOG("Antenna state is changed!");
     mHeadphoneState = aEvent.status();
-    DispatchTrustedEventToSelf(ANTENNA_STATE_CHANGED_EVENT_NAME);
+    DispatchTrustedEvent(ANTENNA_STATE_CHANGED_EVENT_NAME);
   }
 }
 
 void FMRadio::Notify(const FMRadioOperationInformation& info)
 {
   switch (info.operation())
   {
     case FM_RADIO_OPERATION_ENABLE:
-      DispatchTrustedEventToSelf(RADIO_ENABLED_EVENT_NAME);
+      DispatchTrustedEvent(RADIO_ENABLED_EVENT_NAME);
       break;
     case FM_RADIO_OPERATION_DISABLE:
-      DispatchTrustedEventToSelf(RADIO_DIABLED_EVENT_NAME);
+      DispatchTrustedEvent(RADIO_DIABLED_EVENT_NAME);
       break;
     case FM_RADIO_OPERATION_SEEK:
-      DispatchTrustedEventToSelf(RADIO_SEEK_COMPLETE_EVENT_NAME);
+      DispatchTrustedEvent(RADIO_SEEK_COMPLETE_EVENT_NAME);
       break;
   }
 }
-
-nsresult
-FMRadio::DispatchTrustedEventToSelf(const nsAString& aEventName)
-{
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(aEventName,
-                                 /* bubbles = */ false,
-                                 /* cancelable = */ false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
--- a/dom/fm/FMRadio.h
+++ b/dom/fm/FMRadio.h
@@ -38,19 +38,15 @@ public:
   FMRadio();
   virtual void Notify(const hal::FMRadioOperationInformation& info);
   virtual void Notify(const hal::SwitchEvent& aEvent);
 
 private:
   ~FMRadio();
   bool mHasInternalAntenna;
   hal::SwitchState mHeadphoneState;
-  /**
-   * Dispatch a trusted non-cancellable and no-bubbling event to itself
-   */
-  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
 };
 
 } // namespace fm
 } // namespace dom
 } // namespace mozilla
 #endif
 
--- a/dom/icc/src/IccManager.cpp
+++ b/dom/icc/src/IccManager.cpp
@@ -98,17 +98,17 @@ IccManager::Observe(nsISupports* aSubjec
 
     nsresult rv = event->Dispatch(ToIDOMEventTarget(), STKCOMMAND_EVENTNAME);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, kStkSessionEndTopic)) {
-    InternalDispatchEvent(STKSESSIONEND_EVENTNAME);
+    DispatchTrustedEvent(STKSESSIONEND_EVENTNAME);
     return NS_OK;
   }
 
   MOZ_NOT_REACHED("Unknown observer topic!");
 
   return NS_OK;
 }
 
@@ -143,32 +143,14 @@ IccManager::SendStkEventDownload(const J
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
   mProvider->SendStkEventDownload(GetOwner(), aEvent);
   return NS_OK;
 }
 
-nsresult
-IccManager::InternalDispatchEvent(const nsAString& aType)
-{
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(aType, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-
 NS_IMPL_EVENT_HANDLER(IccManager, stkcommand)
 NS_IMPL_EVENT_HANDLER(IccManager, stksessionend)
 
 } // namespace icc
 } // namespace dom
 } // namespace mozilla
--- a/dom/icc/src/IccManager.h
+++ b/dom/icc/src/IccManager.h
@@ -38,17 +38,15 @@ private:
   nsCOMPtr<nsIMobileConnectionProvider> mProvider;
 
   nsIDOMEventTarget*
   ToIDOMEventTarget() const
   {
     return static_cast<nsDOMEventTargetHelper*>(
            const_cast<IccManager*>(this));
   }
-
-  nsresult InternalDispatchEvent(const nsAString& aType);
 };
 
 } // namespace icc
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_icc_IccManager_h
--- a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json
+++ b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json
@@ -432,34 +432,14 @@
   "NodeFilter interface: constant SHOW_ENTITY on interface prototype object": true,
   "NodeFilter interface: constant SHOW_PROCESSING_INSTRUCTION on interface prototype object": true,
   "NodeFilter interface: constant SHOW_COMMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT_TYPE on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT_FRAGMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_NOTATION on interface prototype object": true,
   "NodeFilter interface: operation acceptNode(Node)": true,
-  "NodeList interface: existence and properties of interface object": true,
-  "NodeList interface: existence and properties of interface prototype object": true,
-  "NodeList interface: attribute length": true,
-  "NodeList interface: calling item(unsigned long) on document.querySelectorAll(\"script\") with too few arguments must throw TypeError": true,
-  "HTMLCollection interface: existence and properties of interface object": true,
-  "HTMLCollection interface: existence and properties of interface prototype object": true,
-  "HTMLCollection interface: attribute length": true,
-  "HTMLCollection interface: calling item(unsigned long) on document.body.children with too few arguments must throw TypeError": true,
-  "HTMLCollection interface: calling namedItem(DOMString) on document.body.children with too few arguments must throw TypeError": true,
   "DOMStringList interface: existence and properties of interface object": true,
   "DOMStringList interface: existence and properties of interface prototype object": true,
   "DOMStringList interface: existence and properties of interface prototype object's \"constructor\" property": true,
   "DOMStringList interface: attribute length": true,
-  "DOMTokenList interface: existence and properties of interface object": true,
-  "DOMTokenList interface: existence and properties of interface prototype object": true,
-  "DOMTokenList interface: attribute length": true,
-  "Stringification of document.body.classList": true,
-  "DOMTokenList interface: calling item(unsigned long) on document.body.classList with too few arguments must throw TypeError": true,
-  "DOMTokenList interface: calling contains(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
-  "DOMTokenList interface: calling add(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
-  "DOMTokenList interface: calling remove(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
-  "DOMTokenList interface: calling toggle(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
-  "DOMSettableTokenList interface: existence and properties of interface object": true,
-  "DOMSettableTokenList interface: existence and properties of interface prototype object": true,
-  "DOMSettableTokenList interface: attribute value": true
+  "Stringification of document.body.classList": true
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -405,22 +405,22 @@ TabChild::HandlePossibleMetaViewportChan
     return;
   }
 
   float minScale = 1.0f;
 
   nsCOMPtr<nsIDOMElement> htmlDOMElement = do_QueryInterface(document->GetHtmlElement());
   nsCOMPtr<nsIDOMElement> bodyDOMElement = do_QueryInterface(document->GetBodyElement());
 
-  PRInt32 htmlWidth = 0, htmlHeight = 0;
+  int32_t htmlWidth = 0, htmlHeight = 0;
   if (htmlDOMElement) {
     htmlDOMElement->GetScrollWidth(&htmlWidth);
     htmlDOMElement->GetScrollHeight(&htmlHeight);
   }
-  PRInt32 bodyWidth = 0, bodyHeight = 0;
+  int32_t bodyWidth = 0, bodyHeight = 0;
   if (bodyDOMElement) {
     bodyDOMElement->GetScrollWidth(&bodyWidth);
     bodyDOMElement->GetScrollHeight(&bodyHeight);
   }
 
   float pageWidth = NS_MAX(htmlWidth, bodyWidth);
   float pageHeight = NS_MAX(htmlHeight, bodyHeight);
 
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -22,17 +22,17 @@ try {
   kMaxPendingMessages = Services.prefs.getIntPref("dom.messages.maxPendingMessages");
 } catch(e) {
   // getIntPref throws when the pref is not set.
   kMaxPendingMessages = 5;
 }
 
 const kMessages =["SystemMessageManager:GetPending",
                   "SystemMessageManager:Register",
-                  "SystemMessageManager:Unregister"]
+                  "child-process-shutdown"]
 
 function debug(aMsg) {
   //dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
 }
 
 // Implementation of the component used by internal users.
 
 function SystemMessageInternal() {
@@ -45,34 +45,22 @@ function SystemMessageInternal() {
     ppmm.addMessageListener(aMsg, this);
   }).bind(this));
 }
 
 SystemMessageInternal.prototype = {
   sendMessage: function sendMessage(aType, aMessage, aPageURI, aManifestURI) {
     debug("Broadcasting " + aType + " " + JSON.stringify(aMessage));
     if (this._listeners[aManifestURI.spec]) {
-      let i;
-      let listener;
-      for (i = this._listeners[aManifestURI.spec].length - 1; i >= 0; i -= 1) {
-        listener = this._listeners[aManifestURI.spec][i];
-        try {
-          listener.sendAsyncMessage("SystemMessageManager:Message",
-                                     { type: aType,
-                                       msg: aMessage,
-                                       manifest: aManifestURI.spec })
-        } catch (e) {
-          // Remove once 777508 lands.
-          let index;
-          if ((index = this._listeners[aManifestURI.spec].indexOf(listener)) != -1) {
-            this._listeners[aManifestURI.spec].splice(index, 1);
-            dump("Remove dead MessageManager!\n");
-          }
-        }
-      };
+      this._listeners[aManifestURI.spec].forEach(function sendMsg(aListener) {
+        aListener.sendAsyncMessage("SystemMessageManager:Message",
+                                   { type: aType,
+                                     msg: aMessage,
+                                     manifest: aManifestURI.spec })
+      });
     }
 
     this._pages.forEach(function sendMess_openPage(aPage) {
       if (aPage.type != aType ||
           aPage.manifest != aManifestURI.spec ||
           aPage.uri != aPageURI.spec) {
         return;
       }
@@ -82,33 +70,22 @@ SystemMessageInternal.prototype = {
   },
 
   broadcastMessage: function broadcastMessage(aType, aMessage) {
     debug("Broadcasting " + aType + " " + JSON.stringify(aMessage));
     // Find pages that registered an handler for this type.
     this._pages.forEach(function(aPage) {
       if (aPage.type == aType) {
         if (this._listeners[aPage.manifest]) {
-          let i;
-          for (i = this._listeners[aPage.manifest].length - 1; i >= 0; i -= 1) {
-            let listener = this._listeners[aPage.manifest][i];
-            try {
-              listener.sendAsyncMessage("SystemMessageManager:Message",
-                                         { type: aType,
-                                           msg: aMessage,
-                                           manifest: aPage.manifest})
-            } catch (e) {
-              // Remove once 777508 lands.
-              let index;
-              if ((index = this._listeners[aPage.manifest].indexOf(listener)) != -1) {
-                this._listeners[aPage.manifest].splice(index, 1);
-                dump("Remove dead MessageManager!\n");
-              }
-            }
-          };
+          this._listeners[aPage.manifest].forEach(function sendMsg(aListener) {
+            aListener.sendAsyncMessage("SystemMessageManager:Message",
+                                       { type: aType,
+                                         msg: aMessage,
+                                         manifest: aPage.manifest})
+          });
         }
         this._processPage(aPage, aMessage);
       }
     }.bind(this))
   },
 
   registerPage: function registerPage(aType, aPageURI, aManifestURI) {
     if (!aPageURI || !aManifestURI) {
@@ -128,17 +105,17 @@ SystemMessageInternal.prototype = {
         let manifest = msg.manifest;
         debug("Got Register from " + manifest);
         if (!this._listeners[manifest]) {
           this._listeners[manifest] = [];
         }
         this._listeners[manifest].push(aMessage.target);
         debug("listeners for " + manifest + " : " + this._listeners[manifest].length);
         break;
-      case "SystemMessageManager:Unregister":
+      case "child-process-shutdown":
         debug("Got Unregister from " + aMessage.target);
         let mm = aMessage.target;
         for (let manifest in this._listeners) {
           let index = this._listeners[manifest].indexOf(mm);
           while (index != -1) {
             debug("Removing " + mm + " at index " + index);
             this._listeners[manifest].splice(index, 1);
             index = this._listeners[manifest].indexOf(mm);
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -138,17 +138,16 @@ SystemMessageManager.prototype = {
     return pendings[aType].length != 0;
   },
 
   mozHasPendingMessage: function sysMessMgr_hasPendingMessage(aType) {
     return this._getPendingMessages(aType, false);
   },
 
   uninit: function sysMessMgr_uninit()  {
-    cpmm.sendAsyncMessage("SystemMessageManager:Unregister", { });
     this._handlers = null;
     this._pendings =  null;
   },
 
   receiveMessage: function sysMessMgr_receiveMessage(aMessage) {
     debug("receiveMessage " + aMessage.name + " - " +
           aMessage.json.type + " for " + aMessage.json.manifest +
           " (" + this._manifest + ")");
--- a/dom/network/src/Connection.cpp
+++ b/dom/network/src/Connection.cpp
@@ -92,33 +92,16 @@ Connection::GetMetered(bool* aMetered)
     return NS_OK;
   }
 
   *aMetered = Preferences::GetBool(sMeteredPrefName,
                                    sMeteredDefaultValue);
   return NS_OK;
 }
 
-nsresult
-Connection::DispatchTrustedEventToSelf(const nsAString& aEventName)
-{
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(aEventName, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 void
 Connection::UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo)
 {
   mBandwidth = aNetworkInfo.bandwidth();
   mCanBeMetered = aNetworkInfo.canBeMetered();
 }
 
 void
@@ -129,15 +112,15 @@ Connection::Notify(const hal::NetworkInf
 
   UpdateFromNetworkInfo(aNetworkInfo);
 
   if (previousBandwidth == mBandwidth &&
       previousCanBeMetered == mCanBeMetered) {
     return;
   }
 
-  DispatchTrustedEventToSelf(CHANGE_EVENT_NAME);
+  DispatchTrustedEvent(CHANGE_EVENT_NAME);
 }
 
 } // namespace network
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/network/src/Connection.h
+++ b/dom/network/src/Connection.h
@@ -39,21 +39,16 @@ public:
   // For IObserver
   void Notify(const hal::NetworkInformation& aNetworkInfo);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Connection,
                                            nsDOMEventTargetHelper)
 
 private:
   /**
-   * Dispatch a trusted non-cancellable and non-bubbling event to itself.
-   */
-  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
-
-  /**
    * Update the connection information stored in the object using a
    * NetworkInformation object.
    */
   void UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo);
 
   /**
    * If the connection is of a type that can be metered.
    */
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -120,32 +120,32 @@ MobileConnection::Shutdown()
 // nsIObserver
 
 NS_IMETHODIMP
 MobileConnection::Observe(nsISupports* aSubject,
                           const char* aTopic,
                           const PRUnichar* aData)
 {
   if (!strcmp(aTopic, kVoiceChangedTopic)) {
-    InternalDispatchEvent(VOICECHANGE_EVENTNAME);
+    DispatchTrustedEvent(VOICECHANGE_EVENTNAME);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, kDataChangedTopic)) {
-    InternalDispatchEvent(DATACHANGE_EVENTNAME);
+    DispatchTrustedEvent(DATACHANGE_EVENTNAME);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, kCardStateChangedTopic)) {
-    InternalDispatchEvent(CARDSTATECHANGE_EVENTNAME);
+    DispatchTrustedEvent(CARDSTATECHANGE_EVENTNAME);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, kIccInfoChangedTopic)) {
-    InternalDispatchEvent(ICCINFOCHANGE_EVENTNAME);
+    DispatchTrustedEvent(ICCINFOCHANGE_EVENTNAME);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, kUssdReceivedTopic)) {
     nsString ussd;
     ussd.Assign(aData);
     nsRefPtr<USSDReceivedEvent> event = USSDReceivedEvent::Create(ussd);
     NS_ASSERTION(event, "This should never fail!");
@@ -319,28 +319,11 @@ MobileConnection::CancelUSSD(nsIDOMDOMRe
 {
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
   return mProvider->CancelUSSD(GetOwner(), request);
 }
 
-nsresult
-MobileConnection::InternalDispatchEvent(const nsAString& aType)
-{
-  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
-  nsresult rv = event->InitEvent(aType, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 } // namespace network
 } // namespace dom
 } // namespace mozilla
--- a/dom/network/src/MobileConnection.h
+++ b/dom/network/src/MobileConnection.h
@@ -45,17 +45,15 @@ private:
   nsRefPtr<icc::IccManager> mIccManager;
 
   nsIDOMEventTarget*
   ToIDOMEventTarget() const
   {
     return static_cast<nsDOMEventTargetHelper*>(
            const_cast<MobileConnection*>(this));
   }
-
-  nsresult InternalDispatchEvent(const nsAString& aType);
 };
 
 } // namespace network
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_network_MobileConnection_h
--- a/dom/settings/SettingsChangeNotifier.jsm
+++ b/dom/settings/SettingsChangeNotifier.jsm
@@ -24,17 +24,17 @@ const kFromSettingsChangeNotifier      =
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 
 let SettingsChangeNotifier = {
   init: function() {
     debug("init");
     this.children = [];
-    this._messages = ["Settings:Changed", "Settings:RegisterForMessages", "Settings:UnregisterForMessages"];
+    this._messages = ["Settings:Changed", "Settings:RegisterForMessages", "child-process-shutdown"];
     this._messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
     Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   },
 
@@ -63,34 +63,25 @@ let SettingsChangeNotifier = {
       }
       default:
         debug("Wrong observer topic: " + aTopic);
         break;
     }
   },
 
   broadcastMessage: function broadcastMessage(aMsgName, aContent) {
-    let i;
-    for (i = this.children.length - 1; i >= 0; i -= 1) {
-      let msgMgr = this.children[i];
-      try {
-        msgMgr.sendAsyncMessage(aMsgName, aContent);
-      } catch (e) {
-        let index;
-        if ((index = this.children.indexOf(msgMgr)) != -1) {
-          this.children.splice(index, 1);
-          dump("Remove dead MessageManager!\n");
-        }
-      }
-    };
+    debug("Broadast");
+    this.children.forEach(function(msgMgr) {
+      msgMgr.sendAsyncMessage(aMsgName, aContent);
+    });
   },
 
   receiveMessage: function(aMessage) {
     debug("receiveMessage");
-    let msg = aMessage.json;
+    let msg = aMessage.data;
     let mm = aMessage.target;
     switch (aMessage.name) {
       case "Settings:Changed":
         this.broadcastMessage("Settings:Change:Return:OK",
           { key: msg.key, value: msg.value });
         Services.obs.notifyObservers(this, kMozSettingsChangedObserverTopic,
           JSON.stringify({
             key: msg.key,
@@ -99,20 +90,21 @@ let SettingsChangeNotifier = {
           }));
         break;
       case "Settings:RegisterForMessages":
         debug("Register!");
         if (this.children.indexOf(mm) == -1) {
           this.children.push(mm);
         }
         break;
-      case "Settings:UnregisterForMessages":
+      case "child-process-shutdown":
         debug("Unregister");
         let index;
         if ((index = this.children.indexOf(mm)) != -1) {
+          debug("Unregister index: " + index);
           this.children.splice(index, 1);
         }
         break;
       default:
         debug("Wrong message: " + aMessage.name);
     }
   }
 }
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -337,17 +337,16 @@ SettingsManager.prototype = {
     let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings");
     this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
     debug("has privileges :" + this.hasPrivileges);
   },
 
   observe: function(aSubject, aTopic, aData) {
     debug("Topic: " + aTopic);
     if (aTopic == "inner-window-destroyed") {
-      cpmm.sendAsyncMessage("Settings:UnregisterForMessages");
       let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
       if (wId == this.innerWindowID) {
         Services.obs.removeObserver(this, "inner-window-destroyed");
         cpmm.removeMessageListener("Settings:Change:Return:OK", this);
         this._requests = null;
         this._window = null;
         this._innerWindowID = null;
         this._onsettingchange = null;
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -347,24 +347,17 @@ SmsManager::MarkMessageRead(int32_t aId,
 nsresult
 SmsManager::DispatchTrustedSmsEventToSelf(const nsAString& aEventName, nsIDOMMozSmsMessage* aMessage)
 {
   nsRefPtr<nsDOMEvent> event = new SmsEvent(nullptr, nullptr);
   nsresult rv = static_cast<SmsEvent*>(event.get())->Init(aEventName, false,
                                                           false, aMessage);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(event, &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return DispatchTrustedEvent(event);
 }
 
 NS_IMETHODIMP
 SmsManager::Observe(nsISupports* aSubject, const char* aTopic,
                     const PRUnichar* aData)
 {
   if (!strcmp(aTopic, kSmsReceivedObserverTopic)) {
     nsCOMPtr<nsIDOMMozSmsMessage> message = do_QueryInterface(aSubject);
--- a/dom/telephony/Voicemail.cpp
+++ b/dom/telephony/Voicemail.cpp
@@ -95,24 +95,17 @@ NS_IMPL_EVENT_HANDLER(Voicemail, statusc
 NS_IMETHODIMP
 Voicemail::VoicemailNotification(nsIDOMMozVoicemailStatus* aStatus)
 {
   nsRefPtr<VoicemailEvent> event = new VoicemailEvent(nullptr, nullptr);
   nsresult rv = event->InitVoicemailEvent(NS_LITERAL_STRING("statuschanged"),
                                           false, false, aStatus);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = event->SetTrusted(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool dummy;
-  rv = DispatchEvent(static_cast<nsIDOMMozVoicemailEvent*>(event), &dummy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return DispatchTrustedEvent(static_cast<nsIDOMMozVoicemailEvent*>(event));
 }
 
 nsresult
 NS_NewVoicemail(nsPIDOMWindow* aWindow, nsIDOMMozVoicemail** aVoicemail)
 {
   nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
     aWindow :
     aWindow->GetCurrentInnerWindow();
--- a/dom/tests/mochitest/chrome/test_moving_nodeList.xul
+++ b/dom/tests/mochitest/chrome/test_moving_nodeList.xul
@@ -23,19 +23,20 @@ https://bugzilla.mozilla.org/show_bug.cg
       var firstWindow, secondWindow;
       function iframe_loaded() {
         if (!firstWindow || !secondWindow)
           return;
         var nodeList = firstWindow.document.childNodes;
         ok(!("expando" in nodeList), "shouldn't be able to see expandos on the NodeList");
         nodeList = firstWindow.wrappedJSObject.getNodeList();
         ok(("expando" in nodeList), "should be able to see expandos on the NodeList");
-        is(nodeList.selectedIndex, -1, "can access selectedIndex in chrome");
+        options = firstWindow.wrappedJSObject.getOptions();
+        is(options.selectedIndex, -1, "can access selectedIndex in chrome");
         secondWindow.wrappedJSObject.tryToUseNodeList(nodeList, ok);
-        nodeList = document.createElementNS("http://www.w3.org/1999/xhtml", "select").options;
+        nodeList = document.childNodes;
         secondWindow.wrappedJSObject.tryToUseNodeList(nodeList, ok);
         SimpleTest.finish();
       }
 
   ]]></script>
 
   <iframe id="one" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/file_moving_nodeList.html"
           onload="firstWindow = this.contentWindow; iframe_loaded()" />
--- a/dom/tests/mochitest/general/file_moving_nodeList.html
+++ b/dom/tests/mochitest/general/file_moving_nodeList.html
@@ -1,17 +1,18 @@
 <html>
     <head>
         <script>
             document.childNodes.expando = "foo";
 
             function getNodeList() {
-                var nodeList = document.createElement("select").options;
-                nodeList.expando = "foo";
-                return nodeList;
+                return document.childNodes;
+            }
+            function getOptions() {
+                return document.createElement("select").options;
             }
 
             function tryToUseNodeList(nodeList, ok) {
                 function expectException(op, reason) {
                     try {
                         var result = op();
                         ok(false, "should have thrown an exception, got: " + result);
                     } catch (e) {
--- a/editor/libeditor/html/tests/test_bug549262.html
+++ b/editor/libeditor/html/tests/test_bug549262.html
@@ -19,96 +19,115 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 549262 **/
 
 var smoothScrollPref = "general.smoothScroll";
 SpecialPowers.setBoolPref(smoothScrollPref, false);
 SimpleTest.waitForExplicitFinish();
 var win = window.open("file_bug549262.html", "_blank", 
                       "width=600,height=600,scrollbars=yes");
+
+// grab the timer right at the start
+var cwu = SpecialPowers.getDOMWindowUtils(win);
+function step() {
+  cwu.advanceTimeAndRefresh(100);
+}
+
 SimpleTest.waitForFocus(function() {
   // Make sure that pressing Space when a contenteditable element is not focused
   // will scroll the page.
   var ed = win.document.getElementById("editor");
   var sc = win.document.querySelector("a");
   sc.focus();
   is(win.scrollY, 0, "Sanity check");
   synthesizeKey(" ", {}, win);
-  setTimeout(function() {
-    isnot(win.scrollY, 0, "Page is scrolled down");
-    is(ed.textContent, "abc", "The content of the editable element has not changed");
-    var oldY = win.scrollY;
-    synthesizeKey(" ", {shiftKey: true}, win);
-    setTimeout(function() {
-      ok(win.scrollY < oldY, "Page is scrolled up");
-      is(ed.textContent, "abc", "The content of the editable element has not changed");
+
+  step();
+
+  isnot(win.scrollY, 0, "Page is scrolled down");
+  is(ed.textContent, "abc", "The content of the editable element has not changed");
+  var oldY = win.scrollY;
+  synthesizeKey(" ", {shiftKey: true}, win);
+
+  step();
+
+  ok(win.scrollY < oldY, "Page is scrolled up");
+  is(ed.textContent, "abc", "The content of the editable element has not changed");
+
+  // Make sure that pressing Space when a contenteditable element is focused
+  // will not scroll the page, and will edit the element.
+  ed.focus();
+  win.getSelection().collapse(ed.firstChild, 1);
+  oldY = win.scrollY;
+  synthesizeKey(" ", {}, win);
 
-      // Make sure that pressing Space when a contenteditable element is focused
-      // will not scroll the page, and will edit the element.
-      ed.focus();
-      win.getSelection().collapse(ed.firstChild, 1);
-      oldY = win.scrollY;
-      synthesizeKey(" ", {}, win);
-      setTimeout(function() {
-        ok(win.scrollY <= oldY, "Page is not scrolled down");
-        is(ed.textContent, "a bc", "The content of the editable element has changed");
-        sc.focus();
-        synthesizeKey(" ", {}, win);
-        setTimeout(function() {
-          isnot(win.scrollY, 0, "Page is scrolled down");
-          is(ed.textContent, "a bc", "The content of the editable element has not changed");
-          ed.focus();
-          win.getSelection().collapse(ed.firstChild, 3);
-          synthesizeKey(" ", {shiftKey: true}, win);
-          setTimeout(function() {
-            isnot(win.scrollY, 0, "Page is not scrolled up");
-            is(ed.textContent, "a b c", "The content of the editable element has changed");
+  step();
+
+  ok(win.scrollY <= oldY, "Page is not scrolled down");
+  is(ed.textContent, "a bc", "The content of the editable element has changed");
+  sc.focus();
+  synthesizeKey(" ", {}, win);
+
+  step();
+
+  isnot(win.scrollY, 0, "Page is scrolled down");
+  is(ed.textContent, "a bc", "The content of the editable element has not changed");
+  ed.focus();
+  win.getSelection().collapse(ed.firstChild, 3);
+  synthesizeKey(" ", {shiftKey: true}, win);
+
+  step();
+
+  isnot(win.scrollY, 0, "Page is not scrolled up");
+  is(ed.textContent, "a b c", "The content of the editable element has changed");
+
+  // Now let's test the down/up keys
+  sc = document.body;
 
-            // Now let's test the down/up keys
-            sc = document.body;
-            setTimeout(function() {
-              ed.blur();
-              sc.focus();
-              oldY = win.scrollY;
-              synthesizeKey("VK_UP", {}, win);
-              setTimeout(function() {
-                ok(win.scrollY < oldY, "Page is scrolled up");
-                oldY = win.scrollY;
-                ed.focus();
-                win.getSelection().collapse(ed.firstChild, 3);
-                synthesizeKey("VK_UP", {}, win);
-                setTimeout(function() {
-                  is(win.scrollY, oldY, "Page is not scrolled up");
-                  is(win.getSelection().focusNode, ed.firstChild, "Correct element selected");
-                  is(win.getSelection().focusOffset, 0, "Selection should be moved to the beginning");
-                  win.getSelection().removeAllRanges();
-                  synthesizeMouse(sc, 300, 300, {}, win);
-                  synthesizeKey("VK_DOWN", {}, win);
-                  setTimeout(function() {
-                    ok(win.scrollY > oldY, "Page is scrolled down");
-                    ed.focus();
-                    win.getSelection().collapse(ed.firstChild, 3);
-                    oldY = win.scrollY;
-                    synthesizeKey("VK_DOWN", {}, win);
-                    setTimeout(function() {
-                      is(win.scrollY, oldY, "Page is not scrolled down");
-                      is(win.getSelection().focusNode, ed.firstChild, "Correct element selected");
-                      is(win.getSelection().focusOffset, ed.textContent.length, "Selection should be moved to the end");
+  step();
+
+  ed.blur();
+  sc.focus();
+  oldY = win.scrollY;
+  synthesizeKey("VK_UP", {}, win);
+
+  step();
+
+  ok(win.scrollY < oldY, "Page is scrolled up");
+  oldY = win.scrollY;
+  ed.focus();
+  win.getSelection().collapse(ed.firstChild, 3);
+  synthesizeKey("VK_UP", {}, win);
+
+  step();
 
-                      win.close();
-                      SpecialPowers.clearUserPref(smoothScrollPref);
-                      SimpleTest.finish();
-                    }, 20);
-                  }, 20);
-                }, 20);
-              }, 20);
-            }, 20);
-          }, 20);
-        }, 20);
-      }, 20);
-    }, 20);
-  }, 20);
+  is(win.scrollY, oldY, "Page is not scrolled up");
+  is(win.getSelection().focusNode, ed.firstChild, "Correct element selected");
+  is(win.getSelection().focusOffset, 0, "Selection should be moved to the beginning");
+  win.getSelection().removeAllRanges();
+  synthesizeMouse(sc, 300, 300, {}, win);
+  synthesizeKey("VK_DOWN", {}, win);
+
+  step();
+
+  ok(win.scrollY > oldY, "Page is scrolled down");
+  ed.focus();
+  win.getSelection().collapse(ed.firstChild, 3);
+  oldY = win.scrollY;
+  synthesizeKey("VK_DOWN", {}, win);
+
+  step();
+
+  is(win.scrollY, oldY, "Page is not scrolled down");
+  is(win.getSelection().focusNode, ed.firstChild, "Correct element selected");
+  is(win.getSelection().focusOffset, ed.textContent.length, "Selection should be moved to the end");
+
+  win.close();
+  SpecialPowers.clearUserPref(smoothScrollPref);
+  cwu.restoreNormalRefresh();
+
+  SimpleTest.finish();
 }, win);
 
 </script>
 </pre>
 </body>
 </html>
--- a/gfx/2d/Makefile.in
+++ b/gfx/2d/Makefile.in
@@ -134,16 +134,22 @@ DEFINES += -DWIN32 -DINITGUID
 ifdef MOZ_ENABLE_SKIA
 CPPSRCS += \
         ScaledFontWin.cpp \
         $(NULL)
 endif
 endif
 
 include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+# Due to bug 796023, we can't have -DUNICODE and -D_UNICODE; defining those
+# macros changes the type of LOGFONT to LOGFONTW instead of LOGFONTA. This
+# changes the symbol names of exported C++ functions that use LOGFONT.
+DEFINES := $(filter-out -DUNICODE -D_UNICODE,$(DEFINES))
 
 #ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 #CPPSRCS	+= \
 #        DrawTargetCG.cpp \
 #        SourceSurfaceCG.cpp \
 #	$(NULL)
 #
 ## Always link with OpenGL/AGL
deleted file mode 100644
--- a/gfx/2d/basictypes.h
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_BASICTYPES_H_
-#define BASE_BASICTYPES_H_
-
-// Chromium includes a prtypes.h also, but it has been modified to include
-// their build_config.h as well. We can therefore test for both to determine
-// if someone screws up the include order.
-#if defined(prtypes_h___) && !defined(BUILD_BUILD_CONFIG_H_)
-#error You_must_include_basictypes.h_before_prtypes.h!
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-#define NO_NSPR_10_SUPPORT
-#define NO_NSPR_10_SUPPORT_SAVE
-#endif
-
-
-#ifdef NO_NSPR_10_SUPPORT_SAVE
-#undef NO_NSPR_10_SUPPORT_SAVE
-#undef NO_NSPR_10_SUPPORT
-#endif
-
-#ifdef _WIN32
-#undef _WIN32
-#define _WIN32_SAVE
-#endif
-
-
-#ifdef _WIN32_SAVE
-#undef _WIN32_SAVE
-#define _WIN32
-#endif
-
-#include <limits.h>         // So we can set the bounds of our types
-#include <stddef.h>         // For size_t
-#include <string.h>         // for memcpy
-
-//#include "base/port.h"    // Types that only need exist on certain systems
-
-#ifndef COMPILER_MSVC
-// stdint.h is part of C99 but MSVC doesn't have it.
-#include <stdint.h>         // For intptr_t.
-#endif
-typedef uint8_t uint8;
-typedef int16_t int16;
-#if 0
-// A type to represent a Unicode code-point value. As of Unicode 4.0,
-// such values require up to 21 bits.
-// (For type-checking on pointers, make this explicitly signed,
-// and it should always be the signed version of whatever int32 is.)
-typedef signed int         char32;
-
-const uint8  kuint8max  = (( uint8) 0xFF);
-const uint16 kuint16max = ((uint16) 0xFFFF);
-const uint32 kuint32max = ((uint32) 0xFFFFFFFF);
-const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
-const  int8  kint8min   = ((  int8) 0x80);
-const  int8  kint8max   = ((  int8) 0x7F);
-const  int16 kint16min  = (( int16) 0x8000);
-const  int16 kint16max  = (( int16) 0x7FFF);
-const  int32 kint32min  = (( int32) 0x80000000);
-const  int32 kint32max  = (( int32) 0x7FFFFFFF);
-const  int64 kint64min  = (( int64) GG_LONGLONG(0x8000000000000000));
-const  int64 kint64max  = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF));
-#endif
-// Platform- and hardware-dependent printf specifiers
-#  if defined(OS_POSIX)
-#    define __STDC_FORMAT_MACROS 1
-#    include <inttypes.h>           // for 64-bit integer format macros
-#    define PRId64L "I64d"
-#    define PRIu64L "I64u"
-#    define PRIx64L "I64x"
-#  elif defined(OS_WIN)
-#    define PRId64 "I64d"
-#    define PRIu64 "I64u"
-#    define PRIx64 "I64x"
-#    define PRId64L L"I64d"
-#    define PRIu64L L"I64u"
-#    define PRIx64L L"I64x"
-#  endif
-
-// A macro to disallow the copy constructor and operator= functions
-// This should be used in the private: declarations for a class
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&);               \
-  void operator=(const TypeName&)
-
-// An older, deprecated, politically incorrect name for the above.
-#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
-  DISALLOW_COPY_AND_ASSIGN(TypeName)
-
-// The arraysize(arr) macro returns the # of elements in an array arr.
-// The expression is a compile-time constant, and therefore can be
-// used in defining new arrays, for example.  If you use arraysize on
-// a pointer by mistake, you will get a compile-time error.
-//
-// One caveat is that arraysize() doesn't accept any array of an
-// anonymous type or a type defined inside a function.  In these rare
-// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is
-// due to a limitation in C++'s template system.  The limitation might
-// eventually be removed, but it hasn't happened yet.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-template <typename T, size_t N>
-char (&ArraySizeHelper(T (&array)[N]))[N];
-
-// That gcc wants both of these prototypes seems mysterious. VC, for
-// its part, can't decide which to use (another mystery). Matching of
-// template overloads: the final frontier.
-#ifndef _MSC_VER
-template <typename T, size_t N>
-char (&ArraySizeHelper(const T (&array)[N]))[N];
-#endif
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions.  It's less safe than arraysize as it accepts some
-// (although not all) pointers.  Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors.  If you see a compiler error
-//
-//   "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element).  If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array.  Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size.  Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-
-#define ARRAYSIZE_UNSAFE(a) \
-  ((sizeof(a) / sizeof(*(a))) / \
-   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
-
-// Use implicit_cast as a safe version of static_cast or const_cast
-// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
-// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
-// a const pointer to Foo).
-// When you use implicit_cast, the compiler checks that the cast is safe.
-// Such explicit implicit_casts are necessary in surprisingly many
-// situations where C++ demands an exact type match instead of an
-// argument type convertable to a target type.
-//
-// The From type can be inferred, so the preferred syntax for using
-// implicit_cast is the same as for static_cast etc.:
-//
-//   implicit_cast<ToType>(expr)
-//
-// implicit_cast would have been part of the C++ standard library,
-// but the proposal was submitted too late.  It will probably make
-// its way into the language in the future.
-template<typename To, typename From>
-inline To implicit_cast(From const &f) {
-  return f;
-}
-
-// The COMPILE_ASSERT macro can be used to verify that a compile time
-// expression is true. For example, you could use it to verify the
-// size of a static array:
-//
-//   COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
-//                  content_type_names_incorrect_size);
-//
-// or to make sure a struct is smaller than a certain size:
-//
-//   COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
-//
-// The second argument to the macro is the name of the variable. If
-// the expression is false, most compilers will issue a warning/error
-// containing the name of the variable.
-
-template <bool>
-struct CompileAssert {
-};
-
-#undef COMPILE_ASSERT
-#define COMPILE_ASSERT(expr, msg) \
-  typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-
-// Implementation details of COMPILE_ASSERT:
-//
-// - COMPILE_ASSERT works by defining an array type that has -1
-//   elements (and thus is invalid) when the expression is false.
-//
-// - The simpler definition
-//
-//     #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
-//
-//   does not work, as gcc supports variable-length arrays whose sizes
-//   are determined at run-time (this is gcc's extension and not part
-//   of the C++ standard).  As a result, gcc fails to reject the
-//   following code with the simple definition:
-//
-//     int foo;
-//     COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
-//                               // not a compile-time constant.
-//
-// - By using the type CompileAssert<(bool(expr))>, we ensures that
-//   expr is a compile-time constant.  (Template arguments must be
-//   determined at compile-time.)
-//
-// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
-//   to work around a bug in gcc 3.4.4 and 4.0.1.  If we had written
-//
-//     CompileAssert<bool(expr)>
-//
-//   instead, these compilers will refuse to compile
-//
-//     COMPILE_ASSERT(5 > 0, some_message);
-//
-//   (They seem to think the ">" in "5 > 0" marks the end of the
-//   template argument list.)
-//
-// - The array size is (bool(expr) ? 1 : -1), instead of simply
-//
-//     ((expr) ? 1 : -1).
-//
-//   This is to avoid running into a bug in MS VC 7.1, which
-//   causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
-
-
-// MetatagId refers to metatag-id that we assign to
-// each metatag <name, value> pair..
-//typedef uint32 MetatagId;
-
-// Argument type used in interfaces that can optionally take ownership
-// of a passed in argument.  If TAKE_OWNERSHIP is passed, the called
-// object takes ownership of the argument.  Otherwise it does not.
-enum Ownership {
-  DO_NOT_TAKE_OWNERSHIP,
-  TAKE_OWNERSHIP
-};
-
-// bit_cast<Dest,Source> is a template function that implements the
-// equivalent of "*reinterpret_cast<Dest*>(&source)".  We need this in
-// very low-level functions like the protobuf library and fast math
-// support.
-//
-//   float f = 3.14159265358979;
-//   int i = bit_cast<int32>(f);
-//   // i = 0x40490fdb
-//
-// The classical address-casting method is:
-//
-//   // WRONG
-//   float f = 3.14159265358979;            // WRONG
-//   int i = * reinterpret_cast<int*>(&f);  // WRONG
-//
-// The address-casting method actually produces undefined behavior
-// according to ISO C++ specification section 3.10 -15 -.  Roughly, this
-// section says: if an object in memory has one type, and a program
-// accesses it with a different type, then the result is undefined
-// behavior for most values of "different type".
-//
-// This is true for any cast syntax, either *(int*)&f or
-// *reinterpret_cast<int*>(&f).  And it is particularly true for
-// conversions betweeen integral lvalues and floating-point lvalues.
-//
-// The purpose of 3.10 -15- is to allow optimizing compilers to assume
-// that expressions with different types refer to different memory.  gcc
-// 4.0.1 has an optimizer that takes advantage of this.  So a
-// non-conforming program quietly produces wildly incorrect output.
-//
-// The problem is not the use of reinterpret_cast.  The problem is type
-// punning: holding an object in memory of one type and reading its bits
-// back using a different type.
-//
-// The C++ standard is more subtle and complex than this, but that
-// is the basic idea.
-//
-// Anyways ...
-//
-// bit_cast<> calls memcpy() which is blessed by the standard,
-// especially by the example in section 3.9 .  Also, of course,
-// bit_cast<> wraps up the nasty logic in one place.
-//
-// Fortunately memcpy() is very fast.  In optimized mode, with a
-// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
-// code with the minimal amount of data movement.  On a 32-bit system,
-// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
-// compiles to two loads and two stores.
-//
-// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
-//
-// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
-// is likely to surprise you.
-
-template <class Dest, class Source>
-inline Dest bit_cast(const Source& source) {
-  // Compile time assertion: sizeof(Dest) == sizeof(Source)
-  // A compile error here means your Dest and Source have different sizes.
-  typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
-
-  Dest dest;
-  memcpy(&dest, &source, sizeof(dest));
-  return dest;
-}
-
-// The following enum should be used only as a constructor argument to indicate
-// that the variable has static storage class, and that the constructor should
-// do nothing to its state.  It indicates to the reader that it is legal to
-// declare a static instance of the class, provided the constructor is given
-// the base::LINKER_INITIALIZED argument.  Normally, it is unsafe to declare a
-// static variable that has a constructor or a destructor because invocation
-// order is undefined.  However, IF the type can be initialized by filling with
-// zeroes (which the loader does for static variables), AND the destructor also
-// does nothing to the storage, AND there are no virtual methods, then a
-// constructor declared as
-//       explicit MyClass(base::LinkerInitialized x) {}
-// and invoked as
-//       static MyClass my_variable_name(base::LINKER_INITIALIZED);
-namespace base {
-enum LinkerInitialized { LINKER_INITIALIZED };
-}  // base
-
-
-
-
-#endif  // BASE_BASICTYPES_H_
--- a/gfx/2d/convolver.h
+++ b/gfx/2d/convolver.h
@@ -3,19 +3,19 @@
 // found in the LICENSE file.
 
 #ifndef SKIA_EXT_CONVOLVER_H_
 #define SKIA_EXT_CONVOLVER_H_
 
 #include <cmath>
 #include <vector>
 
-#include "basictypes.h"
+#include "base/basictypes.h"