Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEhsan Akhgari <ehsan@mozilla.com>
Sun, 09 Dec 2012 13:17:02 -0500
changeset 115438 725eb8792d279d645d22df16d1ff0434055c84ca
parent 115332 4e83d0987a31013e303e706361a84552d34af79b (current diff)
parent 115437 cc3df492214453772ebccc3d4f86e45a353d6dbb (diff)
child 115439 ee311a1282efb84780c02d81ffade05077375ac7
child 115448 7a62a67b04019dab934c0e9685a82509e646cc20
push id24003
push usereakhgari@mozilla.com
push dateSun, 09 Dec 2012 18:17:18 +0000
treeherdermozilla-central@725eb8792d27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.0a1
first release with
nightly linux32
725eb8792d27 / 20.0a1 / 20121210030747 / files
nightly linux64
725eb8792d27 / 20.0a1 / 20121210030747 / files
nightly mac
725eb8792d27 / 20.0a1 / 20121210030747 / files
nightly win32
725eb8792d27 / 20.0a1 / 20121210030747 / files
nightly win64
725eb8792d27 / 20.0a1 / 20121210030747 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
accessible/src/jsat/Presenters.jsm
browser/components/tabview/test/browser_tabview_privatebrowsing.js
configure.in
dom/interfaces/html/nsIDOMHTMLPropertiesCollection.idl
dom/interfaces/html/nsIDOMPropertyNodeList.idl
image/test/reftest/generic/accept-image-catchall-ref.html
image/test/reftest/generic/accept-image-catchall.html
image/test/reftest/generic/check-header.sjs
image/test/reftest/generic/green.png
memory/build/extraMallocFuncs.c
memory/mozjemalloc/jemalloc.h
netwerk/base/src/nsSocketTransport2.cpp
toolkit/components/places/nsIPlacesImportExportService.idl
toolkit/components/places/nsPlacesExportService.cpp
toolkit/components/places/nsPlacesExportService.h
toolkit/components/places/tests/favicons/favicon-normal16.png
toolkit/components/places/tests/favicons/test_setAndFetchFaviconForPage_failures.js
--- a/Makefile.in
+++ b/Makefile.in
@@ -41,23 +41,17 @@ tier_base_dirs += \
 endif
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 tier_base_dirs += \
   other-licenses/android \
   $(NULL)
 endif
 
 ifdef MOZ_MEMORY
-tier_base_dirs += memory/mozjemalloc
-ifdef MOZ_JEMALLOC
-ifndef MOZ_NATIVE_JEMALLOC
-tier_base_dirs += memory/jemalloc
-endif
-endif
-tier_base_dirs += memory/build
+tier_base_dirs += memory
 endif
 ifndef MOZ_NATIVE_ZLIB
 tier_base_dirs += modules/zlib
 endif
 tier_base_dirs += \
   mozglue \
   memory/mozalloc \
   $(NULL)
--- a/accessible/src/jsat/AccessFu.css
+++ b/accessible/src/jsat/AccessFu.css
@@ -23,8 +23,40 @@
 
 #accessfu-glass {
   width: 100%;
   height: 100%;
   position: fixed;
   top: 0px;
   left: 0px;
 }
+
+#announce-box {
+  position: fixed;
+  width: 7.5em;
+  height: 5em;
+  top: calc(100% - 50% - 2.5em);
+  left: calc(100% - 50% - 3.75em);
+  pointer-events: none;
+  display: table;
+  font-size: 28pt;
+  font-weight: 700;
+  color: orange;
+  background-color: black;
+  border-radius: 0.25em;
+}
+
+#announce-box:not(.showing) {
+  opacity: 0.0;
+  margin: 0.1em;
+  -moz-transition: opacity 0.4s linear;
+}
+
+#announce-box.showing {
+  opacity: 1.0;
+  -moz-transition: opacity 0.2s linear;
+}
+
+#announce-box * {
+  text-align: center;
+  display: table-cell;
+  vertical-align: middle;
+}
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -67,16 +67,17 @@ this.AccessFu = {
    */
   _enable: function _enable() {
     if (this._enabled)
       return;
     this._enabled = true;
 
     Cu.import('resource://gre/modules/accessibility/Utils.jsm');
     Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
+    Cu.import('resource://gre/modules/accessibility/Presentation.jsm');
 
     Logger.info('enable');
 
     this.touchAdapter = (Utils.MozBuildApp == 'mobile/android') ?
       AndroidTouchAdapter : TouchAdapter;
 
     for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
       this._loadFrameScript(mm);
@@ -140,33 +141,40 @@ this.AccessFu = {
   },
 
   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;
+        let mm = Utils.getMessageManager(aMessage.target);
+        mm.sendAsyncMessage('AccessFu:Start',
+                            {method: 'start', buildApp: Utils.MozBuildApp});
+        break;
       case 'AccessFu:Present':
+        this._output(aMessage.json, aMessage.target);
+        break;
+      case 'AccessFu:Input':
+        Input.setEditState(aMessage.json);
+        break;
+    }
+  },
+
+  _output: function _output(aPresentationData, aBrowser) {
       try {
-        for each (let presenter in aMessage.json) {
-          Output[presenter.type](presenter.details, aMessage.target);
+        for each (let presenter in aPresentationData) {
+          if (!presenter)
+            continue;
+
+          Output[presenter.type](presenter.details, aBrowser);
         }
       } catch (x) {
         Logger.logException(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(
@@ -235,16 +243,21 @@ this.AccessFu = {
               mm.sendAsyncMessage('AccessFu:VirtualCursor', {action: 'whereIsIt'});
             }, 500);
         }
         break;
       }
     }
   },
 
+  announce: function announce(aAnnouncement) {
+    this._output(Presentation.announce(aAnnouncement),
+                 Utils.getCurrentBrowser(this.chromeWin));
+  },
+
   // So we don't enable/disable twice
   _enabled: false,
 
   // Layerview is focused
   _focused: false
 };
 
 var Output = {
@@ -254,44 +267,81 @@ var Output = {
   },
 
   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';
+    switch (aDetails.method) {
+      case 'showBounds':
+      {
+        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';
+          // 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);
+        }
+
+        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';
 
-      this.highlightBox.appendChild(inset);
-    }
-
-    if (aDetails.method == 'show') {
-      let padding = aDetails.padding;
-      let r = this._adjustBounds(aDetails.bounds, aBrowser);
+        break;
+      }
+      case 'hideBounds':
+      {
+        if (this.highlightBox)
+          this.highlightBox.style.display = 'none';
+        break;
+      }
+      case 'showAnnouncement':
+      {
+        if (!this.announceBox) {
+          this.announceBox = this.chromeWin.document.
+            createElementNS('http://www.w3.org/1999/xhtml', 'div');
+          this.announceBox.id = 'announce-box';
+          this.chromeWin.document.documentElement.appendChild(this.announceBox);
+        }
 
-      // 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';
+        this.announceBox.innerHTML = '<div>' + aDetails.text + '</div>';
+        this.announceBox.classList.add('showing');
+
+        if (this._announceHideTimeout)
+          this.chromeWin.clearTimeout(this._announceHideTimeout);
+
+        if (aDetails.duration > 0)
+          this._announceHideTimeout = this.chromeWin.setTimeout(
+            function () {
+              this.announceBox.classList.remove('showing');
+              this._announceHideTimeout = 0;
+            }.bind(this), aDetails.duration);
+        break;
+      }
+      case 'hideAnnouncement':
+      {
+        this.announceBox.classList.remove('showing');
+        break;
+      }
     }
   },
 
   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) {
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -3,59 +3,47 @@
  * 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/Presentation.jsm');
 Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 this.EXPORTED_SYMBOLS = ['EventManager'];
 
 this.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());
-          this.presenters.push(new HapticPresenter());
-        } 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(', '));
+        Logger.info('EventManager.start', Utils.MozBuildApp);
 
         this._started = true;
         Services.obs.addObserver(this, 'accessible-event', false);
       }
 
-      this.present(
-        function(p) {
-          return p.tabStateChanged(null, 'newtab');
-        }
-      );
+      this.present(Presentation.tabStateChanged(null, 'newtab'));
+
     } catch (x) {
       Logger.error('Failed to start EventManager');
       Logger.logException(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':
       {
@@ -64,39 +52,31 @@ this.EventManager = {
         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');
-          }
-        );
+        this.present(Presentation.actionInvoked(activatedAcc, 'click'));
         break;
       }
       case 'scroll':
       case 'resize':
       {
         // the target could be an element, document or window
         let window = null;
         if (aEvent.target instanceof Ci.nsIDOMWindow)
           window = aEvent.target;
         else if (aEvent.target instanceof Ci.nsIDOMDocument)
           window = aEvent.target.defaultView;
         else if (aEvent.target instanceof Ci.nsIDOMElement)
           window = aEvent.target.ownerDocument.defaultView;
-        this.present(
-          function(p) {
-            return p.viewportChanged(window);
-          }
-        );
+        this.present(Presentation.viewportChanged(window));
         break;
       }
       }
     } catch (x) {
       Logger.error('Error handling DOM event');
       Logger.logException(x);
     }
   },
@@ -126,41 +106,35 @@ this.EventManager = {
       {
         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);
-          }
-        );
+          Presentation.pivotChanged(position, event.oldAccessible, 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');
-            }
-          );
+            Presentation.
+              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;
@@ -182,21 +156,17 @@ this.EventManager = {
           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);
-            }
-          );
+          this.present(Presentation.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;
@@ -216,22 +186,19 @@ this.EventManager = {
             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);
-            }
-          );
+          this.present(Presentation, 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;
@@ -240,36 +207,23 @@ this.EventManager = {
           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.logException(x);
-    }
+  present: function present(aPresentationData) {
+    this.sendMsgFunc("AccessFu:Present", aPresentationData);
   },
 
   presentVirtualCursorPosition: function presentVirtualCursorPosition(aVirtualCursor) {
-    let presenterContext =
-      new PresenterContext(aVirtualCursor.position, null);
-
-    this.present(
-      function(p) {
-        return p.pivotChanged(presenterContext, Ci.nsIAccessiblePivot.REASON_NONE);
-      }
-    );
+    this.present(Presentation.pivotChanged(aVirtualCursor.position, null,
+                                           Ci.nsIAccessiblePivot.REASON_NONE));
   },
 
   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 |
@@ -279,33 +233,25 @@ this.EventManager = {
       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);
-        }
-      );
+      this.present(Presentation.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');
-      }
-    );
+    this.present(Presentation.tabStateChanged(docAcc, 'newdoc'));
   },
 
   onStatusChange: function onStatusChange() {},
 
   onSecurityChange: function onSecurityChange() {},
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference,
--- a/accessible/src/jsat/Makefile.in
+++ b/accessible/src/jsat/Makefile.in
@@ -11,17 +11,17 @@ include $(DEPTH)/config/autoconf.mk
 
 INSTALL_TARGETS += ACCESSFU
 
 ACCESSFU_FILES := \
   AccessFu.jsm \
   EventManager.jsm \
   jar.mn \
   Makefile.in \
-  Presenters.jsm \
+  Presentation.jsm \
   TouchAdapter.jsm \
   TraversalRules.jsm \
   Utils.jsm \
   UtteranceGenerator.jsm \
   $(NULL)
 
 ACCESSFU_DEST = $(FINAL_TARGET)/modules/accessibility
 
new file mode 100644
--- /dev/null
+++ b/accessible/src/jsat/Presentation.jsm
@@ -0,0 +1,574 @@
+/* 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;
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import('resource://gre/modules/accessibility/UtteranceGenerator.jsm');
+Cu.import('resource://gre/modules/Geometry.jsm');
+
+this.EXPORTED_SYMBOLS = ['Presentation'];
+
+/**
+ * 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.
+   */
+  detach: function detach() {},
+
+  /**
+   * The virtual cursor's position changed.
+   * @param {PresenterContext} aContext the context object for the new pivot
+   *   position.
+   * @param {int} aReason the reason for the pivot change.
+   *   See nsIAccessiblePivot.
+   */
+  pivotChanged: function pivotChanged(aContext, aReason) {},
+
+  /**
+   * An object's action has been invoked.
+   * @param {nsIAccessible} aObject the object that has been invoked.
+   * @param {string} aActionName the name of the action.
+   */
+  actionInvoked: function actionInvoked(aObject, aActionName) {},
+
+  /**
+   * Text has changed, either by the user or by the system. TODO.
+   */
+  textChanged: function textChanged(aIsInserted, aStartOffset,
+                                    aLength, aText,
+                                    aModifiedText) {},
+
+  /**
+   * Text selection has changed. TODO.
+   */
+  textSelectionChanged: function textSelectionChanged() {},
+
+  /**
+   * Selection has changed. TODO.
+   * @param {nsIAccessible} aObject the object that has been selected.
+   */
+  selectionChanged: function selectionChanged(aObject) {},
+
+  /**
+   * The tab, or the tab's document state has changed.
+   * @param {nsIAccessible} aDocObj the tab document accessible that has had its
+   *    state changed, or null if the tab has no associated document yet.
+   * @param {string} aPageState the state name for the tab, valid states are:
+   *    'newtab', 'loading', 'newdoc', 'loaded', 'stopped', and 'reload'.
+   */
+  tabStateChanged: function tabStateChanged(aDocObj, aPageState) {},
+
+  /**
+   * The current tab has changed.
+   * @param {PresenterContext} aDocContext context object for tab's
+   *   document.
+   * @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(aWindow) {},
+
+  /**
+   * We have entered or left text editing mode.
+   */
+  editingModeChanged: function editingModeChanged(aIsEditing) {},
+
+  /**
+   * Announce something. Typically an app state change.
+   */
+  announce: function announce(aAnnouncement) {}
+};
+
+/**
+ * Visual presenter. Draws a box around the virtual cursor's position.
+ */
+
+this.VisualPresenter = function VisualPresenter() {};
+
+VisualPresenter.prototype = {
+  __proto__: Presenter.prototype,
+
+  type: 'Visual',
+
+  /**
+   * The padding in pixels between the object and the highlight border.
+   */
+  BORDER_PADDING: 2,
+
+  viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
+    if (this._currentAccessible) {
+      let context = new PresenterContext(this._currentAccessible);
+      return {
+        type: this.type,
+        details: {
+          method: 'showBounds',
+          bounds: context.bounds,
+          padding: this.BORDER_PADDING
+        }
+      };
+    }
+
+    return null;
+  },
+
+  pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
+    this._currentAccessible = aContext.accessible;
+
+    if (!aContext.accessible)
+      return {type: this.type, details: {method: 'hideBounds'}};
+
+    try {
+      aContext.accessible.scrollTo(
+        Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
+      return {
+        type: this.type,
+        details: {
+          method: 'showBounds',
+          bounds: aContext.bounds,
+          padding: this.BORDER_PADDING
+        }
+      };
+    } catch (e) {
+      Logger.error('Failed to get bounds: ' + e);
+      return null;
+    }
+  },
+
+  tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
+    return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
+  },
+
+  tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
+                                                            aPageState) {
+    if (aPageState == 'newdoc')
+      return {type: this.type, details: {method: 'hideBounds'}};
+
+    return null;
+  },
+
+  announce: function VisualPresenter_announce(aAnnouncement) {
+    return {
+      type: this.type,
+      details: {
+        method: 'showAnnouncement',
+        text: aAnnouncement,
+        duration: 1000
+      }
+    };
+  }
+};
+
+/**
+ * Android presenter. Fires Android a11y events.
+ */
+
+this.AndroidPresenter = 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,
+
+  pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
+    if (!aContext.accessible)
+      return null;
+
+    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.
+      androidEvents.push({eventType: this.ANDROID_VIEW_HOVER_EXIT, text: []});
+    }
+
+    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));
+      }
+    );
+
+    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.announce(
+      UtteranceGenerator.genForTabStateChange(aDocObj, aPageState).join(' '));
+  },
+
+  textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
+                                                     aLength, aText,
+                                                     aModifiedText) {
+    let eventDetails = {
+      eventType: this.ANDROID_VIEW_TEXT_CHANGED,
+      text: [aText],
+      fromIndex: aStart,
+      removedCount: 0,
+      addedCount: 0
+    };
+
+    if (aIsInserted) {
+      eventDetails.addedCount = aLength;
+      eventDetails.beforeText =
+        aText.substring(0, aStart) + aText.substring(aStart + aLength);
+    } else {
+      eventDetails.removedCount = aLength;
+      eventDetails.beforeText =
+        aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
+    }
+
+    return {type: this.type, details: [eventDetails]};
+  },
+
+  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.announce(
+      UtteranceGenerator.genForEditingMode(aIsEditing).join(' '));
+  },
+
+  announce: function AndroidPresenter_announce(aAnnouncement) {
+    return {
+      type: this.type,
+      details: [{
+        eventType: (Utils.AndroidSdkVersion >= 16) ?
+          this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
+        text: [aAnnouncement],
+        addedCount: aAnnouncement.length,
+        removedCount: 0,
+        fromIndex: 0
+      }]
+    };
+  }
+};
+
+/**
+ * A speech presenter for direct TTS output
+ */
+
+this.SpeechPresenter = 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}}
+        ]
+      }
+    };
+  }
+};
+
+/**
+ * A haptic presenter
+ */
+
+this.HapticPresenter = function HapticPresenter() {};
+
+HapticPresenter.prototype = {
+  __proto__: Presenter.prototype,
+
+  type: 'Haptic',
+
+  PIVOT_CHANGE_PATTHERN: [20],
+
+  pivotChanged: function HapticPresenter_pivotChanged(aContext, aReason) {
+    return { type: this.type, details: { pattern: this.PIVOT_CHANGE_PATTHERN } };
+  }
+};
+
+/**
+ * PresenterContext: An object that generates and caches context information
+ * for a given accessible and its relationship with another accessible.
+ */
+this.PresenterContext = function PresenterContext(aAccessible, aOldAccessible) {
+  this._accessible = aAccessible;
+  this._oldAccessible =
+    this._isDefunct(aOldAccessible) ? null : aOldAccessible;
+}
+
+PresenterContext.prototype = {
+  get accessible() {
+    return this._accessible;
+  },
+
+  get oldAccessible() {
+    return this._oldAccessible;
+  },
+
+  /*
+   * This is a list of the accessible's ancestry up to the common ancestor
+   * of the accessible and the old accessible. It is useful for giving the
+   * user context as to where they are in the heirarchy.
+   */
+  get newAncestry() {
+    if (!this._newAncestry) {
+      let newLineage = [];
+      let oldLineage = [];
+
+      let parent = this._accessible;
+      while (parent && (parent = parent.parent))
+        newLineage.push(parent);
+
+      parent = this._oldAccessible;
+      while (parent && (parent = parent.parent))
+        oldLineage.push(parent);
+
+      this._newAncestry = [];
+
+      while (true) {
+        let newAncestor = newLineage.pop();
+        let oldAncestor = oldLineage.pop();
+
+        if (newAncestor == undefined)
+          break;
+
+        if (newAncestor != oldAncestor)
+          this._newAncestry.push(newAncestor);
+      }
+
+    }
+
+    return this._newAncestry;
+  },
+
+  /*
+   * This is a flattened list of the accessible's subtree in preorder.
+   * It only includes the accessible's visible chidren.
+   */
+  get subtreePreorder() {
+    function traversePreorder(aAccessible) {
+      let list = [];
+      let child = aAccessible.firstChild;
+      while (child) {
+        let state = {};
+        child.getState(state, {});
+
+        if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
+          list.push(child);
+          list.push.apply(list, traversePreorder(child));
+        }
+
+        child = child.nextSibling;
+      }
+      return list;
+    }
+
+    if (!this._subtreePreOrder)
+      this._subtreePreOrder = traversePreorder(this._accessible);
+
+    return this._subtreePreOrder;
+  },
+
+  get bounds() {
+    if (!this._bounds) {
+      let objX = {}, objY = {}, objW = {}, objH = {};
+
+      this._accessible.getBounds(objX, objY, objW, objH);
+
+      // 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);
+    }
+
+    return this._bounds.clone();
+  },
+
+  _isDefunct: function _isDefunct(aAccessible) {
+    try {
+      let extstate = {};
+      aAccessible.getState({}, extstate);
+      return !!(aAccessible.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
+    } catch (x) {
+      return true;
+    }
+  }
+};
+
+this.Presentation = {
+  get presenters() {
+    delete this.presenters;
+    this.presenters = [new VisualPresenter()];
+
+    if (Utils.MozBuildApp == 'b2g') {
+      this.presenters.push(new SpeechPresenter());
+      this.presenters.push(new HapticPresenter());
+    } else if (Utils.MozBuildApp == 'mobile/android') {
+      this.presenters.push(new AndroidPresenter());
+    }
+
+    return this.presenters;
+  },
+
+  pivotChanged: function Presentation_pivotChanged(aPosition,
+                                                   aOldPosition,
+                                                   aReason) {
+    let context = new PresenterContext(aPosition, aOldPosition);
+    return [p.pivotChanged(context, aReason)
+              for each (p in this.presenters)];
+  },
+
+  actionInvoked: function Presentation_actionInvoked(aObject, aActionName) {
+    return [p.actionInvoked(aObject, aActionName)
+              for each (p in this.presenters)];
+  },
+
+  textChanged: function Presentation_textChanged(aIsInserted, aStartOffset,
+                                    aLength, aText,
+                                    aModifiedText) {
+    return [p.textChanged(aIsInserted, aStartOffset, aLength,
+                          aText, aModifiedText)
+              for each (p in this.presenters)];
+  },
+
+  tabStateChanged: function Presentation_tabStateChanged(aDocObj, aPageState) {
+    return [p.tabStateChanged(aDocObj, aPageState)
+              for each (p in this.presenters)];
+  },
+
+  viewportChanged: function Presentation_viewportChanged(aWindow) {
+    return [p.viewportChanged(aWindow)
+              for each (p in this.presenters)];
+  },
+
+  editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
+    return [p.editingModeChanged(aIsEditing)
+              for each (p in this.presenters)];
+  },
+
+  announce: function Presentation_announce(aAnnouncement) {
+    // XXX: Typically each presenter uses the UtteranceGenerator,
+    // but there really isn't a point here.
+    return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
+              for each (p in this.presenters)];
+  }
+};
deleted file mode 100644
--- a/accessible/src/jsat/Presenters.jsm
+++ /dev/null
@@ -1,506 +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;
-
-Cu.import('resource://gre/modules/accessibility/Utils.jsm');
-Cu.import('resource://gre/modules/accessibility/UtteranceGenerator.jsm');
-Cu.import('resource://gre/modules/Geometry.jsm');
-
-this.EXPORTED_SYMBOLS = ['VisualPresenter',
-                         'AndroidPresenter',
-                         'DummyAndroidPresenter',
-                         'SpeechPresenter',
-                         'HapticPresenter',
-                         'PresenterContext'];
-
-/**
- * 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.
-   */
-  detach: function detach() {},
-
-  /**
-   * The virtual cursor's position changed.
-   * @param {PresenterContext} aContext the context object for the new pivot
-   *   position.
-   * @param {int} aReason the reason for the pivot change.
-   *   See nsIAccessiblePivot.
-   */
-  pivotChanged: function pivotChanged(aContext, aReason) {},
-
-  /**
-   * An object's action has been invoked.
-   * @param {nsIAccessible} aObject the object that has been invoked.
-   * @param {string} aActionName the name of the action.
-   */
-  actionInvoked: function actionInvoked(aObject, aActionName) {},
-
-  /**
-   * Text has changed, either by the user or by the system. TODO.
-   */
-  textChanged: function textChanged(aIsInserted, aStartOffset,
-                                    aLength, aText,
-                                    aModifiedText) {},
-
-  /**
-   * Text selection has changed. TODO.
-   */
-  textSelectionChanged: function textSelectionChanged() {},
-
-  /**
-   * Selection has changed. TODO.
-   * @param {nsIAccessible} aObject the object that has been selected.
-   */
-  selectionChanged: function selectionChanged(aObject) {},
-
-  /**
-   * The tab, or the tab's document state has changed.
-   * @param {nsIAccessible} aDocObj the tab document accessible that has had its
-   *    state changed, or null if the tab has no associated document yet.
-   * @param {string} aPageState the state name for the tab, valid states are:
-   *    'newtab', 'loading', 'newdoc', 'loaded', 'stopped', and 'reload'.
-   */
-  tabStateChanged: function tabStateChanged(aDocObj, aPageState) {},
-
-  /**
-   * The current tab has changed.
-   * @param {PresenterContext} aDocContext context object for tab's
-   *   document.
-   * @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(aWindow) {},
-
-  /**
-   * We have entered or left text editing mode.
-   */
-  editingModeChanged: function editingModeChanged(aIsEditing) {}
-};
-
-/**
- * Visual presenter. Draws a box around the virtual cursor's position.
- */
-
-this.VisualPresenter = function VisualPresenter() {};
-
-VisualPresenter.prototype = {
-  __proto__: Presenter.prototype,
-
-  type: 'Visual',
-
-  /**
-   * The padding in pixels between the object and the highlight border.
-   */
-  BORDER_PADDING: 2,
-
-  viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
-    if (this._currentAccessible) {
-      let context = new PresenterContext(this._currentAccessible);
-      return {
-        type: this.type,
-        details: {
-          method: 'show',
-          bounds: context.bounds,
-          padding: this.BORDER_PADDING
-        }
-      };
-    }
-
-    return null;
-  },
-
-  pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
-    this._currentAccessible = aContext.accessible;
-
-    if (!aContext.accessible)
-      return {type: this.type, details: {method: 'hide'}};
-
-    try {
-      aContext.accessible.scrollTo(
-        Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
-      return {
-        type: this.type,
-        details: {
-          method: 'show',
-          bounds: aContext.bounds,
-          padding: this.BORDER_PADDING
-        }
-      };
-    } catch (e) {
-      Logger.error('Failed to get bounds: ' + e);
-      return null;
-    }
-  },
-
-  tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
-    return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
-  },
-
-  tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
-                                                            aPageState) {
-    if (aPageState == 'newdoc')
-      return {type: this.type, details: {method: 'hide'}};
-
-    return null;
-  }
-};
-
-/**
- * Android presenter. Fires Android a11y events.
- */
-
-this.AndroidPresenter = 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,
-
-  pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
-    if (!aContext.accessible)
-      return null;
-
-    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.
-      androidEvents.push({eventType: this.ANDROID_VIEW_HOVER_EXIT, text: []});
-    }
-
-    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));
-      }
-    );
-
-    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 eventDetails = {
-      eventType: this.ANDROID_VIEW_TEXT_CHANGED,
-      text: [aText],
-      fromIndex: aStart,
-      removedCount: 0,
-      addedCount: 0
-    };
-
-    if (aIsInserted) {
-      eventDetails.addedCount = aLength;
-      eventDetails.beforeText =
-        aText.substring(0, aStart) + aText.substring(aStart + aLength);
-    } else {
-      eventDetails.removedCount = aLength;
-      eventDetails.beforeText =
-        aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
-    }
-
-    return {type: this.type, details: [eventDetails]};
-  },
-
-  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
-      }]
-    };
-  }
-};
-
-/**
- * A speech presenter for direct TTS output
- */
-
-this.SpeechPresenter = 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}}
-        ]
-      }
-    };
-  }
-};
-
-/**
- * A haptic presenter
- */
-
-this.HapticPresenter = function HapticPresenter() {};
-
-HapticPresenter.prototype = {
-  __proto__: Presenter.prototype,
-
-  type: 'Haptic',
-
-  PIVOT_CHANGE_PATTHERN: [20],
-
-  pivotChanged: function HapticPresenter_pivotChanged(aContext, aReason) {
-    return { type: this.type, details: { pattern: this.PIVOT_CHANGE_PATTHERN } };
-  }
-};
-
-/**
- * PresenterContext: An object that generates and caches context information
- * for a given accessible and its relationship with another accessible.
- */
-this.PresenterContext = function PresenterContext(aAccessible, aOldAccessible) {
-  this._accessible = aAccessible;
-  this._oldAccessible =
-    this._isDefunct(aOldAccessible) ? null : aOldAccessible;
-}
-
-PresenterContext.prototype = {
-  get accessible() {
-    return this._accessible;
-  },
-
-  get oldAccessible() {
-    return this._oldAccessible;
-  },
-
-  /*
-   * This is a list of the accessible's ancestry up to the common ancestor
-   * of the accessible and the old accessible. It is useful for giving the
-   * user context as to where they are in the heirarchy.
-   */
-  get newAncestry() {
-    if (!this._newAncestry) {
-      let newLineage = [];
-      let oldLineage = [];
-
-      let parent = this._accessible;
-      while (parent && (parent = parent.parent))
-        newLineage.push(parent);
-
-      parent = this._oldAccessible;
-      while (parent && (parent = parent.parent))
-        oldLineage.push(parent);
-
-      this._newAncestry = [];
-
-      while (true) {
-        let newAncestor = newLineage.pop();
-        let oldAncestor = oldLineage.pop();
-
-        if (newAncestor == undefined)
-          break;
-
-        if (newAncestor != oldAncestor)
-          this._newAncestry.push(newAncestor);
-      }
-
-    }
-
-    return this._newAncestry;
-  },
-
-  /*
-   * This is a flattened list of the accessible's subtree in preorder.
-   * It only includes the accessible's visible chidren.
-   */
-  get subtreePreorder() {
-    function traversePreorder(aAccessible) {
-      let list = [];
-      let child = aAccessible.firstChild;
-      while (child) {
-        let state = {};
-        child.getState(state, {});
-
-        if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
-          list.push(child);
-          list.push.apply(list, traversePreorder(child));
-        }
-
-        child = child.nextSibling;
-      }
-      return list;
-    }
-
-    if (!this._subtreePreOrder)
-      this._subtreePreOrder = traversePreorder(this._accessible);
-
-    return this._subtreePreOrder;
-  },
-
-  get bounds() {
-    if (!this._bounds) {
-      let objX = {}, objY = {}, objW = {}, objH = {};
-
-      this._accessible.getBounds(objX, objY, objW, objH);
-
-      // 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);
-    }
-
-    return this._bounds.clone();
-  },
-
-  _isDefunct: function _isDefunct(aAccessible) {
-    try {
-      let extstate = {};
-      aAccessible.getState({}, extstate);
-      return !!(aAccessible.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
-    } catch (x) {
-      return true;
-    }
-  }
-};
--- a/accessible/src/jsat/UtteranceGenerator.jsm
+++ b/accessible/src/jsat/UtteranceGenerator.jsm
@@ -93,16 +93,30 @@ this.UtteranceGenerator = {
    *    {@link gActionMap}.
    * @return {Array} A one string array with the action.
    */
   genForAction: function genForAction(aObject, aActionName) {
     return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])];
   },
 
   /**
+   * Generates an utterance for an announcement. Basically attempts to localize
+   * the announcement string.
+   * @param {string} aAnnouncement unlocalized announcement.
+   * @return {Array} A one string array with the announcement.
+   */
+  genForAnnouncement: function genForAnnouncement(aAnnouncement) {
+    try {
+      return [gStringBundle.GetStringFromName(aAnnouncement)];
+    } catch (x) {
+      return [aAnnouncement];
+    }
+  },
+
+  /**
    * Generates an utterance for a tab state change.
    * @param {nsIAccessible} aAccessible accessible object of the tab's attached
    *    document.
    * @param {string} aTabState the tab state name, see
    *    {@link Presenter.tabStateChanged}.
    * @return {Array} The tab state utterace.
    */
   genForTabStateChange: function genForTabStateChange(aObject, aTabState) {
@@ -304,17 +318,17 @@ this.UtteranceGenerator = {
     }
 
     if (aStates.base & Ci.nsIAccessibleStates.STATE_TRAVERSED) {
       stateUtterances.push(gStringBundle.GetStringFromName('stateTraversed'));
     }
 
     return stateUtterances;
   },
-  
+
   _getListUtterance: function _getListUtterance(aAccessible, aRoleStr, aFlags, aItemCount) {
     let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
     let desc = [];
     let roleStr = this._getLocalizedRole(aRoleStr);
     if (roleStr)
       desc.push(roleStr);
     desc.push
       (gStringBundle.formatStringFromName('listItemCount', [aItemCount], 1));
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -117,17 +117,18 @@ nsAccessNodeWrap::QueryService(REFGUID g
 
     *ppv = static_cast<IAccessible*>(docAcc);
 
     (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
     return S_OK;
   }
 
   // Can get to IAccessibleApplication from any node via QS
-  if (guidService == IID_IAccessibleApplication) {
+  if (guidService == IID_IAccessibleApplication ||
+      (Compatibility::IsJAWS() && iid == IID_IAccessibleApplication)) {
     ApplicationAccessible* applicationAcc = ApplicationAcc();
     if (!applicationAcc)
       return E_NOINTERFACE;
 
     nsresult rv = applicationAcc->QueryNativeInterface(iid, ppv);
     return NS_SUCCEEDED(rv) ? S_OK : E_NOINTERFACE;
   }
 
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -54,27 +54,43 @@ if [ ! "$LIBXUL_SDK" ]; then
       build/stlport/stl/config/_android.h
     "
   fi
   add_makefiles "
     memory/mozalloc/Makefile
     mozglue/Makefile
     mozglue/build/Makefile
   "
-  if [ "$MOZ_JEMALLOC" -a -z "$MOZ_NATIVE_JEMALLOC" ]; then
+  if [ "$MOZ_JEMALLOC3" -o "$MOZ_REPLACE_MALLOC" ] && [ -z "$MOZ_NATIVE_JEMALLOC" ]; then
     add_makefiles "
       memory/jemalloc/Makefile
     "
   fi
   if [ "$MOZ_MEMORY" ]; then
     add_makefiles "
+      memory/Makefile
       memory/mozjemalloc/Makefile
       memory/build/Makefile
     "
   fi
+  if [ "$MOZ_REPLACE_MALLOC" ]; then
+    add_makefiles "
+      memory/replace/Makefile
+    "
+    if [ -z "$MOZ_JEMALLOC3" ]; then
+      add_makefiles "
+        memory/replace/jemalloc/Makefile
+      "
+    fi
+  fi
+  if [ "$MOZ_REPLACE_MALLOC_LINKAGE" = "dummy library" ]; then
+    add_makefiles "
+      memory/replace/dummy/Makefile
+    "
+  fi
   if [ "$MOZ_WIDGET_TOOLKIT" = "android" ]; then
     add_makefiles "
       other-licenses/android/Makefile
       other-licenses/skia-npapi/Makefile
       mozglue/android/Makefile
     "
   fi
   if [ "$MOZ_LINKER" ]; then
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -129,16 +129,23 @@ var shell = {
       return;
     }
 
     Services.obs.addObserver(function observer(subject, topic, state) {
       let network = subject.QueryInterface(Ci.nsINetworkInterface);
       if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
           && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
         shell.CrashSubmit.submit(aCrashID);
+
+        // purge the queue.
+        let pending = shell.CrashSubmit.pendingIDs();
+        for (let crashid of pending) {
+          shell.CrashSubmit.submit(crashid);
+        }
+
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-interface-state-changed", false);
   },
 
   get contentBrowser() {
     delete this.contentBrowser;
     return this.contentBrowser = document.getElementById('homescreen');
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -86,17 +86,17 @@
 #ifdef MOZ_UPDATER
           <description class="text-blurb" id="currentChannelText">
             &channel.description.start;<label id="currentChannel"/>&channel.description.end;
           </description>
 #endif
           <vbox id="experimental" hidden="true">
             <description class="text-blurb" id="warningDesc">
               &warningDesc.version;
-#ifdef MOZ_TELEMETRY_REPORTING
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
               &warningDesc.telemetryDesc;
 #endif
             </description>
             <description class="text-blurb" id="communityExperimentalDesc">
               &community.exp.start;<label class="text-link" href="http://www.mozilla.org/">&community.exp.mozillaLink;</label>&community.exp.middle;<label class="text-link" href="about:credits">&community.exp.creditsLink;</label>&community.exp.end;
             </description>
           </vbox>
           <description class="text-blurb" id="communityDesc">
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -14,17 +14,17 @@ html {
 
 body {
   margin: 0;
   display: -moz-box;
   -moz-box-orient: vertical;
   width: 100%;
   height: 100%;
   background-image: url(chrome://browser/content/abouthome/noise.png),
-                    -moz-linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4));
+                    linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4));
 }
 
 input,
 button {
   font-size: inherit;
   font-family: inherit;
 }
 
@@ -88,17 +88,17 @@ a {
 
 #searchText:focus,
 #searchText[autofocus] {
   border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
 }
 
 #searchSubmit {
   -moz-margin-start: -1px;
-  background: -moz-linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
+  background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
   padding: 0 9px;
   border: 1px solid;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
   -moz-border-start: 1px solid transparent;
   border-radius: 0 2.5px 2.5px 0;
   box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
               0 1px 0 hsla(0,0%,100%,.2);
   cursor: pointer;
@@ -114,24 +114,24 @@ a {
 #searchText + #searchSubmit:hover,
 #searchText[autofocus] + #searchSubmit {
   border-color: #59b5fc #45a3e7 #3294d5;
   color: white;
 }
 
 #searchText:focus + #searchSubmit,
 #searchText[autofocus] + #searchSubmit {
-  background-image: -moz-linear-gradient(#4cb1ff, #1793e5);
+  background-image: linear-gradient(#4cb1ff, #1793e5);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03);
 }
 
 #searchText + #searchSubmit:hover {
-  background-image: -moz-linear-gradient(#66bdff, #0d9eff);
+  background-image: linear-gradient(#66bdff, #0d9eff);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03),
               0 0 4px hsla(206,100%,20%,.2);
 }
 
 #searchText + #searchSubmit:hover:active {
   box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
@@ -218,48 +218,48 @@ body[narrow] #launcher[session] > .launc
 }
 
 .launchButton:hover {
   background-color: hsla(211,79%,6%,.03);
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
 }
 
 .launchButton:hover:active {
-  background-image: -moz-linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
+  background-image: linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
   border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25);
   box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset,
               0 0 1px hsla(211,79%,6%,.1) inset;
   transition-duration: 0ms;
 }
 
 .launchButton[hidden],
 #launcher:not([session]) > #restorePreviousSessionSeparator,
 #launcher:not([session]) > #restorePreviousSession {
   display: none;
 }
 
 #restorePreviousSessionSeparator {
   width: 3px;
   height: 116px;
   margin: 0 10px;
-  background-image: -moz-linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
-                    -moz-linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
-                    -moz-linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
+  background-image: linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
+                    linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
+                    linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
   background-position: left top, center, right bottom;
   background-size: 1px auto;
   background-repeat: no-repeat;
 }
 
 body[narrow] #restorePreviousSessionSeparator {
   margin: 0 auto;
   width: 512px;
   height: 3px;
-  background-image: -moz-linear-gradient(0, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
-                    -moz-linear-gradient(0, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
-                    -moz-linear-gradient(0, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
+  background-image: linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)),
+                    linear-gradient(to right, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)),
+                    linear-gradient(to right, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0));
   background-size: auto 1px;
 }
 
 #restorePreviousSession {
   max-width: none;
   font-size: 90%;
 }
 
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -1,13 +1,17 @@
 # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 # 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/.
 
+const PLUGIN_SCRIPTED_STATE_NONE = 0;
+const PLUGIN_SCRIPTED_STATE_FIRED = 1;
+const PLUGIN_SCRIPTED_STATE_DONE = 2;
+
 function getPluginInfo(pluginElement)
 {
   var tagMimetype;
   var pluginsPage;
   var pluginName = gNavigatorBundle.getString("pluginInfo.unknownPlugin");
   if (pluginElement instanceof HTMLAppletElement) {
     tagMimetype = "application/x-java-vm";
   } else {
@@ -222,26 +226,71 @@ var gPluginHandler = {
       case "PluginPlayPreview":
         self._handlePlayPreviewEvent(plugin);
         break;
 
       case "PluginDisabled":
         let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
         self.addLinkClickCallback(manageLink, "managePlugins");
         break;
+
+      case "PluginScripted":
+        let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
+        if (browser._pluginScriptedState == PLUGIN_SCRIPTED_STATE_NONE) {
+          browser._pluginScriptedState = PLUGIN_SCRIPTED_STATE_FIRED;
+          setTimeout(function() {
+            gPluginHandler.handlePluginScripted(this);
+          }.bind(browser), 500);
+        }
+        break;
     }
 
     // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed") {
       let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
       if (overlay != null && self.isTooSmall(plugin, overlay))
           overlay.style.visibility = "hidden";
     }
   },
 
+  handlePluginScripted: function PH_handlePluginScripted(aBrowser) {
+    let contentWindow = aBrowser.contentWindow;
+    if (!contentWindow)
+      return;
+
+    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    let haveVisibleCTPPlugin = cwu.plugins.some(function(plugin) {
+      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      let doc = plugin.ownerDocument;
+      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      if (!overlay)
+        return false;
+
+      // if the plugin's style is 240x200, it's a good bet we set that in
+      // toolkit/mozapps/plugins/content/pluginProblemContent.css
+      // (meaning this plugin was never actually given a size, so it's really
+      // not part of visible content)
+      let computedStyle = contentWindow.getComputedStyle(plugin);
+      let isInvisible = ((computedStyle.width == "240px" &&
+                          computedStyle.height == "200px") ||
+                         gPluginHandler.isTooSmall(plugin, overlay));
+      return (!isInvisible &&
+              gPluginHandler.canActivatePlugin(objLoadingContent));
+    });
+
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
+    if (notification && !haveVisibleCTPPlugin) {
+      notification.dismissed = false;
+      PopupNotifications._update(notification.anchorElement);
+    }
+
+    aBrowser._pluginScriptedState = PLUGIN_SCRIPTED_STATE_DONE;
+  },
+
   isKnownPlugin: function PH_isKnownPlugin(objLoadingContent) {
     return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
             Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
   },
 
   canActivatePlugin: function PH_canActivatePlugin(objLoadingContent) {
     // if this isn't a known plugin, we can't activate it
     // (this also guards pluginHost.getPermissionStringForType against
@@ -586,17 +635,19 @@ var gPluginHandler = {
       callback: function () {
         gPluginHandler._setPermissionForPlugins(aBrowser, Ci.nsIPermissionManager.DENY_ACTION, cwu.plugins);
         let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
         if (notification)
           notification.remove();
         gPluginHandler._removeClickToPlayOverlays(contentWindow);
       }
     }];
-    let options = { dismissed: true, centerActions: centerActions };
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
+    let dismissed = notification ? notification.dismissed : true;
+    let options = { dismissed: dismissed, centerActions: centerActions };
     PopupNotifications.show(aBrowser, "click-to-play-plugins",
                             messageString, "plugins-notification-icon",
                             mainAction, secondaryActions, options);
   },
 
   _removeClickToPlayOverlays: function PH_removeClickToPlayOverlays(aContentWindow) {
     let doc = aContentWindow.document;
     let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -12,17 +12,16 @@ const nsIWebNavigation = Ci.nsIWebNaviga
 
 var gCharsetMenu = null;
 var gLastBrowserCharset = null;
 var gPrevCharset = null;
 var gProxyFavIcon = null;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
-var gStartupRan = false;
 
 #ifndef XP_MACOSX
 var gEditUIVisible = true;
 #endif
 
 [
   ["gBrowser",            "content"],
   ["gNavToolbox",         "navigator-toolbox"],
@@ -987,16 +986,17 @@ var gBrowserInit = {
       var uriToLoad = window.arguments[0];
 
     var mustLoadSidebar = false;
 
     gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
 
     // Note that the XBL binding is untrusted
     gBrowser.addEventListener("PluginBindingAttached", gPluginHandler, true, true);
+    gBrowser.addEventListener("PluginScripted",        gPluginHandler, true);
     gBrowser.addEventListener("PluginCrashed",         gPluginHandler, true);
     gBrowser.addEventListener("PluginOutdated",        gPluginHandler, true);
 
     gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
 #ifdef XP_MACOSX
     gBrowser.addEventListener("npapi-carbon-event-model-failure", gPluginHandler, true);
 #endif
 
@@ -1175,17 +1175,17 @@ var gBrowserInit = {
     TabsInTitlebar.init();
     gPrivateBrowsingUI.init();
     retrieveToolbarIconsizesFromTheme();
 
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
-    gStartupRan = true;
+    this._loadHandled = true;
   },
 
   _cancelDelayedStartup: function () {
     window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
     this._boundDelayedStartup = null;
   },
 
   _delayedStartup: function(uriToLoad, mustLoadSidebar) {
@@ -1495,17 +1495,17 @@ var gBrowserInit = {
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
   onUnload: function() {
     // In certain scenarios it's possible for unload to be fired before onload,
     // (e.g. if the window is being closed after browser.js loads but before the
     // load completes). In that case, there's nothing to do here.
-    if (!gStartupRan)
+    if (!this._loadHandled)
       return;
 
     gDevTools.forgetBrowserWindow(window);
 
     // First clean up services initialized in gBrowserInit.onLoad (or those whose
     // uninit methods don't depend on the services having been initialized).
     allTabs.uninit();
 
@@ -1656,18 +1656,16 @@ var gBrowserInit = {
 
     // initialize the private browsing UI
     gPrivateBrowsingUI.init();
 
 #ifdef MOZ_SERVICES_SYNC
     // initialize the sync UI
     gSyncUI.init();
 #endif
-
-    gStartupRan = true;
   },
 
   nonBrowserWindowShutdown: function() {
     // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
     // just cancel the pending timeout and return;
     if (this._delayedStartupTimeoutId) {
       clearTimeout(this._delayedStartupTimeoutId);
       return;
@@ -4515,16 +4513,17 @@ var TabsProgressListener = {
     // Filter out any sub-frame loads
     if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
       // Filter out any onLocationChanges triggered by anchor navigation
       // or history.push/pop/replaceState.
       if (aRequest) {
         // Initialize the click-to-play state.
         aBrowser._clickToPlayDoorhangerShown = false;
         aBrowser._clickToPlayPluginsActivated = false;
+        aBrowser._pluginScriptedState = PLUGIN_SCRIPTED_STATE_NONE;
       }
       FullZoom.onLocationChange(aLocationURI, false, aBrowser);
     }
   },
 
   onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
     if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
       let brandBundle = document.getElementById("bundle_brand");
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -181,16 +181,17 @@ endif
                  browser_hide_removing.js \
                  browser_overflowScroll.js \
                  browser_locationBarCommand.js \
                  browser_locationBarExternalLoad.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
+                 browser_clickToPlayPluginScriptAccessPopup.js \
                  browser_pluginplaypreview.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
                  browser_clearplugindata.js \
                  browser_clearplugindata.html \
                  browser_clearplugindata_noage.html \
@@ -233,16 +234,23 @@ endif
                  video.ogg \
                  test_bug435035.html \
                  test_bug462673.html \
                  page_style_sample.html \
                  plugin_unknown.html \
                  plugin_test.html \
                  plugin_test2.html \
                  plugin_test3.html \
+                 plugin_test_noScriptNoPopup.html \
+                 plugin_test_scriptedPopup1.html \
+                 plugin_test_scriptedPopup2.html \
+                 plugin_test_scriptedPopup3.html \
+                 plugin_test_scriptedNoPopup1.html \
+                 plugin_test_scriptedNoPopup2.html \
+                 plugin_test_scriptedNoPopup3.html \
                  plugin_alternate_content.html \
                  plugin_both.html \
                  plugin_both2.html \
                  plugin_bug743421.html \
                  plugin_clickToPlayAllow.html \
                  plugin_clickToPlayDeny.html \
                  plugin_bug749455.html \
                  plugin_bug797677.html \
--- a/browser/base/content/test/browser_bug676619.js
+++ b/browser/base/content/test/browser_bug676619.js
@@ -1,46 +1,58 @@
 function test () {
   waitForExplicitFinish();
+
+  var isHTTPS = false;
+
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function () {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    if (isHTTPS) {
+      gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    }
     let doc = gBrowser.contentDocument;
 
 
     function testLocation(link, url, next) {
       var tabOpenListener = new TabOpenListener(url, function () {
           gBrowser.removeTab(this.tab);
       }, function () {
         next();
       });
 
       doc.getElementById(link).click();
     }
 
     function testLink(link, name, next) {
         addWindowListener("chrome://mozapps/content/downloads/unknownContentType.xul", function (win) {
+            is(doc.getElementById("unload-flag").textContent, "Okay", "beforeunload shouldn't have fired");
             is(win.document.getElementById("location").value, name, "file name should match");
             win.close();
             next();
         });
 
         doc.getElementById(link).click();
     }
 
     testLink("link1", "test.txt",
       testLink.bind(null, "link2", "video.ogg",
         testLink.bind(null, "link3", "just some video",
           testLink.bind(null, "link4", "with-target.txt",
             testLink.bind(null, "link5", "javascript.txt",
               testLink.bind(null, "link6", "test.blob",
                 testLocation.bind(null, "link7", "http://example.com/",
                   function () {
-                    gBrowser.removeCurrentTab();
-                    finish();
+                    if (isHTTPS) {
+                      gBrowser.removeCurrentTab();
+                      finish();
+                    } else {
+                      // same test again with https:
+                      isHTTPS = true;
+                      content.location = "https://example.com:443/browser/browser/base/content/test/download_page.html";
+                    }
                   })))))));
 
   }, true);
 
   content.location = "http://mochi.test:8888/browser/browser/base/content/test/download_page.html";
 }
 
 
@@ -101,9 +113,9 @@ TabOpenListener.prototype = {
       this.opencallback = null;
       this.tab = null;
       this.browser = null;
       // Let the window close complete
       executeSoon(this.closecallback);
       this.closecallback = null;
     }
   }
-};
\ No newline at end of file
+};
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_clickToPlayPluginScriptAccessPopup.js
@@ -0,0 +1,123 @@
+const gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+const EXPECTED_PLUGINSCRIPTED_EVENT_COUNT = 6;
+
+var gTestBrowser = null;
+var gNextTestList = [];
+var gNextTest = null;
+var gPluginScriptedFired = false;
+var gPluginScriptedFiredCount = 0;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("plugins.click_to_play");
+    gTestBrowser.removeEventListener("load", pageLoad, true);
+    gTestBrowser.removeEventListener("PluginScripted", pluginScripted, true);
+  });
+  Services.prefs.setBoolPref("plugins.click_to_play", true);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gTestBrowser = gBrowser.selectedBrowser;
+  gTestBrowser.addEventListener("load", pageLoad, true);
+  gTestBrowser.addEventListener("PluginScripted", pluginScripted, true);
+
+  gNextTestList = [
+    { func: testExpectPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedPopup1.html" },
+    { func: testExpectPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedPopup2.html" },
+    { func: testExpectPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedPopup3.html" },
+    { func: testExpectNoPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedNoPopup1.html" },
+    { func: testExpectNoPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedNoPopup2.html" },
+    { func: testExpectNoPopupPart1,
+      url: gHttpTestRoot + "plugin_test_scriptedNoPopup3.html" }
+  ];
+
+  prepareTest(testNoEventFired, gHttpTestRoot + "plugin_test_noScriptNoPopup.html");
+}
+
+function getCurrentTestLocation() {
+  var loc = gTestBrowser.contentWindow.location.toString();
+  return loc.replace(gHttpTestRoot, "");
+}
+
+function runNextTest() {
+  var nextTest = gNextTestList.pop();
+  if (nextTest) {
+    gPluginScriptedFired = false;
+    prepareTest(nextTest.func, nextTest.url);
+  }
+  else {
+    finishTest();
+  }
+}
+
+function finishTest() {
+  is(gPluginScriptedFiredCount, EXPECTED_PLUGINSCRIPTED_EVENT_COUNT, "PluginScripted event count is correct");
+  gBrowser.removeCurrentTab();
+  window.focus();
+  finish();
+}
+
+function pluginScripted() {
+  gPluginScriptedFired = true;
+  gPluginScriptedFiredCount++;
+}
+
+function pageLoad() {
+  // The plugin events are async dispatched and can come after the load event
+  // This just allows the events to fire before we then go on to test the states
+  executeSoon(gNextTest);
+}
+
+function prepareTest(nextTest, url) {
+  gNextTest = nextTest;
+  gTestBrowser.contentWindow.location = url;
+}
+
+function testNoEventFired() {
+  var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(notification, "should have a click-to-play notification (" + getCurrentTestLocation() + ")");
+  ok(notification.dismissed, "notification should not be showing (" + getCurrentTestLocation() + ")");
+  ok(!gPluginScriptedFired, "PluginScripted should not have fired (" + getCurrentTestLocation() + ")");
+
+  runNextTest();
+}
+
+function testExpectNoPopupPart1() {
+  var condition = function() gPluginScriptedFired;
+  waitForCondition(condition, testExpectNoPopupPart2, "waited too long for PluginScripted event (" + getCurrentTestLocation() + ")");
+}
+
+function testExpectNoPopupPart2() {
+  var condition = function() gTestBrowser._pluginScriptedState == PLUGIN_SCRIPTED_STATE_DONE;
+  waitForCondition(condition, testExpectNoPopupPart3, "waited too long for PluginScripted event handling (" + getCurrentTestLocation() + ")");
+}
+
+function testExpectNoPopupPart3() {
+  var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(notification, "should have a click-to-play notification (" + getCurrentTestLocation() + ")");
+  ok(notification.dismissed, "notification should not be showing (" + getCurrentTestLocation() + ")");
+
+  runNextTest();
+}
+
+function testExpectPopupPart1() {
+  var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(notification, "should have a click-to-play notification (" + getCurrentTestLocation() + ")");
+
+  var condition = function() !notification.dismissed;
+  waitForCondition(condition, testExpectPopupPart2, "waited too long for popup notification to show (" + getCurrentTestLocation() + ")");
+}
+
+function testExpectPopupPart2() {
+  var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(!notification.dismissed, "notification should be showing (" + getCurrentTestLocation() + ")");
+
+  runNextTest();
+}
--- a/browser/base/content/test/download_page.html
+++ b/browser/base/content/test/download_page.html
@@ -8,19 +8,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   </head>
   <body>
     <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=676619">Bug 676619</a>
     <br/>
     <ul>
         <li><a href="data:text/plain,Hey What are you looking for?"
                 download="test.txt" id="link1">Download "test.txt"</a></li>
-        <li><a href="http://mochi.test:8888/browser/browser/base/content/test/video.ogg"
+        <li><a href="video.ogg"
                 download id="link2">Download "video.ogg"</a></li>
-        <li><a href="http://mochi.test:8888/browser/browser/base/content/test/video.ogg"
+        <li><a href="video.ogg"
                 download="just some video" id="link3">Download "just some video"</a></li>
         <li><a href="data:text/plain,test"
                 download="with-target.txt" id="link4">Download "with-target.txt"</a></li>
         <li><a href="javascript:1+2"
             download="javascript.txt" id="link5">Download "javascript.txt"</a></li>
     </ul>
     <script>
         var li = document.createElement('li');
@@ -28,15 +28,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 
         a.href = window.URL.createObjectURL(new Blob(["just text"]))    ;
         a.download = "test.blob";
         a.id = "link6";
         a.textContent = 'Download "test.blob"';
 
         li.appendChild(a);
         document.getElementsByTagName('ul')[0].appendChild(li);
+
+        window.addEventListener("beforeunload", function (evt) {
+            document.getElementById("unload-flag").textContent = "Fail";
+        });
     </script>
     <ul>
         <li><a href="http://example.com/"
                 download="example.com" id="link7" target="_blank">Download "example.com"</a></li>
     <ul>
+    <div id="unload-flag">Okay</div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_noScriptNoPopup.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<embed id="test" type="application/x-test"/>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedNoPopup1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<embed id="test" type="application/x-test"/>
+<embed id="test-visible" type="application/x-test" width=200 height=200/>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+  }
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedNoPopup2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<div style="width: 200px; height: 200px;">
+  <embed id="test" style="width: inherit; height: inherit;" type="application/x-test"/>
+</div>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+  }
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedNoPopup3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<div id="container" style="width: 0px; height: 0px;">
+  <embed id="test" style="width: inherit; height: inherit;" type="application/x-test"/>
+</div>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+
+    setTimeout(function() {
+      var container = document.getElementById("container");
+      container.style.width = "200px";
+      container.style.height = "200px";
+    }, 10);
+  }
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedPopup1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<embed id="test" type="application/x-test"/>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+  }
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedPopup2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<embed id="test" type="application/x-test" width=0 height=0/>
+<embed id="test2" type="application/x-test" width=1 height=1/>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+  }
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_test_scriptedPopup3.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="scriptPlugin()">
+<div id="container" style="width: 0px; height: 0px;">
+  <embed id="test" style="width: inherit; height: inherit;" type="application/x-test"/>
+</div>
+<script>
+  function scriptPlugin() {
+    try {
+      document.getElementById("test").getObjectValue();
+    }
+    catch (e) {}
+  }
+</script>
+</body>
+</html>
--- a/browser/components/migration/src/MigrationUtils.jsm
+++ b/browser/components/migration/src/MigrationUtils.jsm
@@ -261,23 +261,24 @@ this.MigratorPrototype = {
       // from another profile.
       const BOOKMARKS = MigrationUtils.resourceTypes.BOOKMARKS;
       let migratingBookmarks = resources.some(function(r) r.type == BOOKMARKS);
       if (migratingBookmarks) {
         let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].
                           getService(Ci.nsIObserver);
         browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, "");
 
-        // Note doMigrate doesn't care about the success value of the
-        // callback.
+        // Note doMigrate doesn't care about the success of the import.
+        let onImportComplete = function() {
+          browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
+          doMigrate();
+        };
         BookmarkHTMLUtils.importFromURL(
-          "resource:///defaults/profile/bookmarks.html", true, function(a) {
-            browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
-            doMigrate();
-          });
+          "resource:///defaults/profile/bookmarks.html", true).then(
+          onImportComplete, onImportComplete);
         return;
       }
     }
     doMigrate();
   },
 
   /**
    * DO NOT OVERRIDE - After deCOMing migration, this code
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1,8 +1,9 @@
+#filter substitution
 # -*- indent-tabs-mode: nil -*-
 # 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/.
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cr = Components.results;
@@ -18,16 +19,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
                                   "resource://gre/modules/UserAgentOverrides.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
                                   "resource://gre/modules/BookmarkHTMLUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "webappsUI",
                                   "resource:///modules/webappsUI.jsm");
@@ -858,25 +862,28 @@ BrowserGlue.prototype = {
                                      true, url, clickCallback);
     }
     catch (e) {
     }
   },
 
 #ifdef MOZ_TELEMETRY_REPORTING
   _showTelemetryNotification: function BG__showTelemetryNotification() {
-    const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+    const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabledPreRelease";
+    const PREF_TELEMETRY_DISPLAYED = "toolkit.telemetry.notifiedOptOut";
+#else
     const PREF_TELEMETRY_ENABLED  = "toolkit.telemetry.enabled";
+    const PREF_TELEMETRY_DISPLAYED = "toolkit.telemetry.prompted";
+#endif
     const PREF_TELEMETRY_REJECTED  = "toolkit.telemetry.rejected";
     const PREF_TELEMETRY_INFOURL  = "toolkit.telemetry.infoURL";
     const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
-    const PREF_TELEMETRY_ENABLED_BY_DEFAULT = "toolkit.telemetry.enabledByDefault";
-    const PREF_TELEMETRY_NOTIFIED_OPTOUT = "toolkit.telemetry.notifiedOptOut";
-    // This is used to reprompt users when privacy message changes
-    const TELEMETRY_PROMPT_REV = 2;
+    // This is used to reprompt/renotify users when privacy message changes
+    const TELEMETRY_DISPLAY_REV = @MOZ_TELEMETRY_DISPLAY_REV@;
 
     // Stick notifications onto the selected tab of the active browser window.
     var win = this.getMostRecentBrowserWindow();
     var tabbrowser = win.gBrowser;
     var notifyBox = tabbrowser.getNotificationBox();
 
     var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
     var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
@@ -898,93 +905,128 @@ BrowserGlue.prototype = {
       let link = notification.ownerDocument.createElementNS(XULNS, "label");
       link.className = "text-link telemetry-text-link";
       link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
       let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
       description.appendChild(link);
       return link;
     }
 
-    var telemetryEnabledByDefault = false;
+    /*
+     * Display an opt-out notification when telemetry is enabled by default,
+     * an opt-in prompt otherwise.
+     *
+     * But do not display this prompt/notification if:
+     *
+     * - The last accepted/refused policy (either by accepting the prompt or by
+     *   manually flipping the telemetry preference) is already at version
+     *   TELEMETRY_DISPLAY_REV.
+     */
+    var telemetryDisplayed;
     try {
-      telemetryEnabledByDefault = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED_BY_DEFAULT);
+      telemetryDisplayed = Services.prefs.getIntPref(PREF_TELEMETRY_DISPLAYED);
     } catch(e) {}
-    if (telemetryEnabledByDefault) {
-      var telemetryNotifiedOptOut = false;
-      try {
-        telemetryNotifiedOptOut = Services.prefs.getBoolPref(PREF_TELEMETRY_NOTIFIED_OPTOUT);
-      } catch(e) {}
-      if (telemetryNotifiedOptOut)
-        return;
+    if (telemetryDisplayed === TELEMETRY_DISPLAY_REV)
+      return;
 
-      var telemetryPrompt = browserBundle.formatStringFromName("telemetryOptOutPrompt",
-                                                               [productName, serverOwner, productName], 3);
-
-      Services.prefs.setBoolPref(PREF_TELEMETRY_NOTIFIED_OPTOUT, true);
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+    /*
+     * Additionally, in opt-out builds, don't display the notification if:
+     *
+     * - Telemetry is disabled
+     * - Telemetry was explicitly refused through the UI
+     * - Opt-in telemetry was enabled and this is the first run with opt-out.
+     */
 
-      let notification = appendTelemetryNotification(telemetryPrompt, null, false);
-      let link = appendLearnMoreLink(notification);
-      link.addEventListener('click', function() {
-        // Open the learn more url in a new tab
-        let url = Services.urlFormatter.formatURLPref("app.support.baseURL");
-        url += "how-can-i-help-submitting-performance-data";
-        win.openUILinkIn(url, "tab");
-        // Remove the notification on which the user clicked
-        notification.parentNode.removeNotification(notification, true);
-      }, false);
+    var telemetryEnabled = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED);
+    if (!telemetryEnabled)
+      return;
+
+    // If telemetry was explicitly refused through the UI,
+    // also disable opt-out telemetry and bail out.
+    var telemetryRejected = false;
+    try {
+      telemetryRejected = Services.prefs.getBoolPref(PREF_TELEMETRY_REJECTED);
+    } catch(e) {}
+    if (telemetryRejected) {
+      Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
+      Services.prefs.setIntPref(PREF_TELEMETRY_DISPLAYED, TELEMETRY_DISPLAY_REV);
       return;
     }
 
-    var telemetryPrompted = null;
+    // If opt-in telemetry was enabled and this is the first run with opt-out,
+    // don't notify the user.
+    var optInTelemetryEnabled = false;
     try {
-      telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
+      optInTelemetryEnabled = Services.prefs.getBoolPref("toolkit.telemetry.enabled");
     } catch(e) {}
-    // If the user has seen the latest telemetry prompt, do not prompt again
-    // else clear old prefs and reprompt
-    if (telemetryPrompted === TELEMETRY_PROMPT_REV)
+    if (optInTelemetryEnabled && telemetryDisplayed === undefined) {
+      Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, false);
+      Services.prefs.setIntPref(PREF_TELEMETRY_DISPLAYED, TELEMETRY_DISPLAY_REV);
       return;
-    
-    Services.prefs.clearUserPref(PREF_TELEMETRY_PROMPTED);
+    }
+
+    var telemetryPrompt = browserBundle.formatStringFromName("telemetryOptOutPrompt",
+                                                            [productName, serverOwner, productName], 3);
+    var buttons = null;
+    var hideCloseButton = false;
+    function learnModeClickHandler() {
+      // Open the learn more url in a new tab
+      var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
+      url += "how-can-i-help-submitting-performance-data";
+      tabbrowser.selectedTab = tabbrowser.addTab(url);
+      // Remove the notification on which the user clicked
+      notification.parentNode.removeNotification(notification, true);
+    }
+#else
+
+    // Clear old prefs and reprompt
     Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED);
-    
-    var telemetryPrompt = browserBundle.formatStringFromName("telemetryOptInPrompt", [productName, serverOwner], 2);
+    Services.prefs.clearUserPref(PREF_TELEMETRY_REJECTED);
 
+    var telemetryPrompt = browserBundle.formatStringFromName("telemetryOptInPrompt",
+                                                            [productName, serverOwner], 2);
     var buttons = [
                     {
                       label:     browserBundle.GetStringFromName("telemetryYesButtonLabel2"),
                       accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+                        Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, false);
                       }
                     },
                     {
                       label:     browserBundle.GetStringFromName("telemetryNoButtonLabel"),
                       accessKey: browserBundle.GetStringFromName("telemetryNoButtonAccessKey"),
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true);
                       }
                     }
                   ];
 
-    // Set pref to indicate we've shown the notification.
-    Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
-
-    let notification = appendTelemetryNotification(telemetryPrompt, buttons, true);
-    let link = appendLearnMoreLink(notification);
-    link.addEventListener('click', function() {
+    var hideCloseButton = true;
+    function learnModeClickHandler() {
       // Open the learn more url in a new tab
       win.openUILinkIn(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL), "tab");
       // Remove the notification on which the user clicked
       notification.parentNode.removeNotification(notification, true);
       // Add a new notification to that tab, with no "Learn more" link
       notifyBox = tabbrowser.getNotificationBox();
       appendTelemetryNotification(telemetryPrompt, buttons, true);
-    }, false);
+    }
+#endif
+
+    // Set pref to indicate we've shown the notification.
+    Services.prefs.setIntPref(PREF_TELEMETRY_DISPLAYED, TELEMETRY_DISPLAY_REV);
+
+    var notification = appendTelemetryNotification(telemetryPrompt, buttons, hideCloseButton);
+    var link = appendLearnMoreLink(notification);
+    link.addEventListener('click', learnModeClickHandler, false);
   },
 #endif
 
   _showPluginUpdatePage: function BG__showPluginUpdatePage() {
     Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
 
     var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
                     getService(Ci.nsIURLFormatter);
@@ -1113,29 +1155,30 @@ BrowserGlue.prototype = {
         var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
         if (bookmarksFile.exists())
           bookmarksURI = NetUtil.newURI(bookmarksFile);
       }
 
       if (bookmarksURI) {
         // Import from bookmarks.html file.
         try {
-          BookmarkHTMLUtils.importFromURL(bookmarksURI.spec, true, (function (success) {
-            if (success) {
+          BookmarkHTMLUtils.importFromURL(bookmarksURI.spec, true).then(null,
+            function onFailure() {
+              Cu.reportError("Bookmarks.html file could be corrupt.");
+            }
+          ).then(
+            function onComplete() {
               // Now apply distribution customized bookmarks.
               // This should always run after Places initialization.
               this._distributionCustomizer.applyBookmarks();
               // Ensure that smart bookmarks are created once the operation is
               // complete.
               this.ensurePlacesDefaultQueriesInitialized();
-            }
-            else {
-              Cu.reportError("Bookmarks.html file could be corrupt.");
-            }
-          }).bind(this));
+            }.bind(this)
+          );
         } catch (err) {
           Cu.reportError("Bookmarks.html file could be corrupt. " + err);
         }
       }
       else {
         Cu.reportError("Unable to find bookmarks.html file.");
       }
 
@@ -1167,26 +1210,41 @@ BrowserGlue.prototype = {
     if (this._isIdleObserver) {
       this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
       this._isIdleObserver = false;
     }
     this._backupBookmarks();
 
     // Backup bookmarks to bookmarks.html to support apps that depend
     // on the legacy format.
-    var autoExportHTML = false;
     try {
-      autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML");
+      // If this fails to get the preference value, we don't export.
+      if (Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML")) {
+        // Exceptionally, since this is a non-default setting and HTML format is
+        // discouraged in favor of the JSON backups, we spin the event loop on
+        // shutdown, to wait for the export to finish.  We cannot safely spin
+        // the event loop on shutdown until we include a watchdog to prevent
+        // potential hangs (bug 518683).  The asynchronous shutdown operations
+        // will then be handled by a shutdown service (bug 435058).
+        let shutdownComplete = false;
+        BookmarkHTMLUtils.exportToFile(FileUtils.getFile("BMarks", [])).then(
+          function onSuccess() {
+            shutdownComplete = true;
+          },
+          function onFailure() {
+            // There is no point in reporting errors since we are shutting down.
+            shutdownComplete = true;
+          }
+        );
+        let thread = Services.tm.currentThread;
+        while (!shutdownComplete) {
+          thread.processNextEvent(true);
+        }
+      }
     } catch(ex) { /* Don't export */ }
-
-    if (autoExportHTML) {
-      Cc["@mozilla.org/browser/places/import-export-service;1"].
-        getService(Ci.nsIPlacesImportExportService).
-        backupBookmarksFile();
-    }
   },
 
   /**
    * Backup bookmarks if needed.
    */
   _backupBookmarks: function BG__backupBookmarks() {
     let lastBackupFile = PlacesUtils.backups.getMostRecent();
 
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -332,37 +332,37 @@ var PlacesOrganizer = {
   /**
    * Open a file-picker and import the selected file into the bookmarks store
    */
   importFromFile: function PO_importFromFile() {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     let fpCallback = function fpCallback_done(aResult) {
       if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
         Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
-        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
+        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false)
+                         .then(null, Components.utils.reportError);
       }
     };
 
     fp.init(window, PlacesUIUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     fp.open(fpCallback);
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     let fpCallback = function fpCallback_done(aResult) {
       if (aResult != Ci.nsIFilePicker.returnCancel) {
-        let exporter =
-          Cc["@mozilla.org/browser/places/import-export-service;1"].
-            getService(Ci.nsIPlacesImportExportService);
-        exporter.exportHTMLToFile(fp.file);
+        Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+        BookmarkHTMLUtils.exportToFile(fp.file)
+                         .then(null, Components.utils.reportError);
       }
     };
 
     fp.init(window, PlacesUIUtils.getString("EnterExport"),
             Ci.nsIFilePicker.modeSave);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     fp.defaultString = "bookmarks.html";
     fp.open(fpCallback);
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -1,8 +1,9 @@
+#filter substitution
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # 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/.
 
 // Load DownloadUtils module for convertByteUnits
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/ctypes.jsm");
@@ -42,16 +43,19 @@ var gAdvancedPane = {
 
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
     this.initSubmitCrashes();
 #endif
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+    this.initTelemetry();
+#endif
     this.updateActualCacheSize("disk");
     this.updateActualCacheSize("offline");
 
     // Notify observers that the UI is now ready
     Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
   },
 
   /**
@@ -148,16 +152,50 @@ var gAdvancedPane = {
     var checkbox = document.getElementById("submitCrashesBox");
     try {
       var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
                getService(Components.interfaces.nsICrashReporter);
       cr.submitReports = checkbox.checked;
     } catch (e) { }
   },
 
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+  /**
+   * When telemetry is opt-out, verify if the user explicitly rejected the
+   * telemetry prompt, and if so reflect his choice in the current preference
+   * value. This doesn't cover the case where the user refused telemetry in the
+   * prompt but later enabled it in preferences in builds before the fix for
+   * bug 737600.
+   */
+  initTelemetry: function ()
+  {
+    const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabledPreRelease";
+    let enabled = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED);
+    let rejected = false;
+    try {
+      rejected = Services.prefs.getBoolPref("toolkit.telemetry.rejected");
+    } catch (e) {}
+    if (enabled && rejected) {
+      Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
+    }
+  },
+#endif
+
+  /**
+   * When the user toggles telemetry, update the rejected value as well, so we
+   * know he expressed a choice, and don't re-prompt inadvertently.
+   */
+  telemetryEnabledChanged: function (event)
+  {
+    let rejected = document.getElementById("toolkit.telemetry.rejected");
+    rejected.value = !event.target.value;
+    let displayed = document.getElementById("toolkit.telemetry.prompted");
+    displayed.value = @MOZ_TELEMETRY_DISPLAY_REV@;
+  },
+
   /**
    * When the user toggles the layers.acceleration.disabled pref,
    * sync its new value to the gfx.direct2d.disabled pref too.
    */
   updateHardwareAcceleration: function()
   {
 #ifdef XP_WIN
     var fromPref = document.getElementById("layers.acceleration.disabled");
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -45,19 +45,36 @@
                   name="browser.shell.checkDefaultBrowser"
                   type="bool"/>
 
       <preference id="pref.general.disable_button.default_browser"
                   name="pref.general.disable_button.default_browser"
                   type="bool"/>
 #endif
 
+#ifdef MOZ_TELEMETRY_REPORTING
       <preference id="toolkit.telemetry.enabled"
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+                  name="toolkit.telemetry.enabledPreRelease"
+#else
                   name="toolkit.telemetry.enabled"
+#endif
+                  onchange="gAdvancedPane.telemetryEnabledChanged(event);"
                   type="bool"/>
+      <preference id="toolkit.telemetry.prompted"
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+                  name="toolkit.telemetry.notifiedOptOut"
+#else
+                  name="toolkit.telemetry.prompted"
+#endif
+                  type="int"/>
+      <preference id="toolkit.telemetry.rejected"
+                  name="toolkit.telemetry.rejected"
+                  type="bool"/>
+#endif
 
       <!-- Network tab -->
       <preference id="browser.cache.disk.capacity"     name="browser.cache.disk.capacity"     type="int"/>
       <preference id="browser.offline-apps.notify"     name="browser.offline-apps.notify"     type="bool"/>
  
       <preference id="browser.cache.disk.smart_size.enabled"
                   name="browser.cache.disk.smart_size.enabled"
                   inverted="true"
@@ -181,19 +198,21 @@
               </deck>
             </hbox>
 #ifdef MOZ_CRASHREPORTER
             <checkbox id="submitCrashesBox" flex="1"
                       oncommand="gAdvancedPane.updateSubmitCrashes();"
                       label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
 #endif
 #endif
+#ifdef MOZ_TELEMETRY_REPORTING
             <checkbox id="submitTelemetryBox" flex="1"
                       preference="toolkit.telemetry.enabled"
                       label="&submitTelemetry.label;" accesskey="&submitTelemetry.accesskey;"/>
+#endif
           </groupbox>
         </tabpanel>
 
         <!-- Network -->
         <tabpanel id="networkPanel" orient="vertical">
 
           <!-- Connection -->
           <groupbox id="connectionGroup">
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -1,8 +1,9 @@
+#filter substitution
 /* 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/. */
 
 // Load DownloadUtils module for convertByteUnits
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 
 var gAdvancedPane = {
@@ -33,16 +34,19 @@ var gAdvancedPane = {
 #endif
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
     this.initSubmitCrashes();
 #endif
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+    this.initTelemetry();
+#endif
     this.updateActualCacheSize("disk");
     this.updateActualCacheSize("offline");
   },
 
   /**
    * Stores the identity of the current tab in preferences so that the selected
    * tab can be persisted between openings of the preferences window.
    */
@@ -136,16 +140,50 @@ var gAdvancedPane = {
     var checkbox = document.getElementById("submitCrashesBox");
     try {
       var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
                getService(Components.interfaces.nsICrashReporter);
       cr.submitReports = checkbox.checked;
     } catch (e) { }
   },
 
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+  /**
+   * When telemetry is opt-out, verify if the user explicitly rejected the
+   * telemetry prompt, and if so reflect his choice in the current preference
+   * value. This doesn't cover the case where the user refused telemetry in the
+   * prompt but later enabled it in preferences in builds before the fix for
+   * bug 737600.
+   */
+  initTelemetry: function ()
+  {
+    const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabledPreRelease";
+    let enabled = Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED);
+    let rejected = false;
+    try {
+      rejected = Services.prefs.getBoolPref("toolkit.telemetry.rejected");
+    } catch (e) {}
+    if (enabled && rejected) {
+      Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, false);
+    }
+  },
+#endif
+
+  /**
+   * When the user toggles telemetry, update the rejected value as well, so we
+   * know he expressed a choice, and don't re-prompt inadvertently.
+   */
+  telemetryEnabledChanged: function (event)
+  {
+    let rejected = document.getElementById("toolkit.telemetry.rejected");
+    rejected.value = !event.target.value;
+    let displayed = document.getElementById("toolkit.telemetry.prompted");
+    displayed.value = @MOZ_TELEMETRY_DISPLAY_REV@;
+  },
+
   /**
    * When the user toggles the layers.acceleration.disabled pref,
    * sync its new value to the gfx.direct2d.disabled pref too.
    */
   updateHardwareAcceleration: function()
   {
 #ifdef XP_WIN
     var fromPref = document.getElementById("layers.acceleration.disabled");
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -47,19 +47,36 @@
               name="browser.shell.checkDefaultBrowser"
               type="bool"/>
 
   <preference id="pref.general.disable_button.default_browser"
               name="pref.general.disable_button.default_browser"
               type="bool"/>
 #endif
 
+#ifdef MOZ_TELEMETRY_REPORTING
   <preference id="toolkit.telemetry.enabled"
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+              name="toolkit.telemetry.enabledPreRelease"
+#else
               name="toolkit.telemetry.enabled"
+#endif
+              onchange="gAdvancedPane.telemetryEnabledChanged(event);"
               type="bool"/>
+  <preference id="toolkit.telemetry.prompted"
+#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
+              name="toolkit.telemetry.notifiedOptOut"
+#else
+              name="toolkit.telemetry.prompted"
+#endif
+              type="int"/>
+  <preference id="toolkit.telemetry.rejected"
+              name="toolkit.telemetry.rejected"
+              type="bool"/>
+#endif
 
   <!-- Network tab -->
   <preference id="browser.cache.disk.capacity"
               name="browser.cache.disk.capacity"
               type="int"/>
   <preference id="browser.offline-apps.notify"
               name="browser.offline-apps.notify"
               type="bool"/>
@@ -202,19 +219,21 @@
           </deck>
         </hbox>
 #ifdef MOZ_CRASHREPORTER
         <checkbox id="submitCrashesBox" flex="1"
                   oncommand="gAdvancedPane.updateSubmitCrashes();"
                   label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
 #endif
 #endif
+#ifdef MOZ_TELEMETRY_REPORTING
         <checkbox id="submitTelemetryBox" flex="1"
                   preference="toolkit.telemetry.enabled"
                   label="&submitTelemetry.label;" accesskey="&submitTelemetry.accesskey;"/>
+#endif
       </groupbox>
     </tabpanel>
 
     <!-- Network -->
     <tabpanel id="networkPanel" orient="vertical">
 
       <!-- Connection -->
       <groupbox id="connectionGroup">
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -240,17 +240,17 @@ browser.jar:
   skin/classic/browser/devtools/responsive-vertical-resizer.png (devtools/responsive-vertical-resizer.png)
   skin/classic/browser/devtools/responsive-background.png   (devtools/responsive-background.png)
   skin/classic/browser/devtools/tools-icons-small.png       (devtools/tools-icons-small.png)
   skin/classic/browser/devtools/dock-bottom.png             (devtools/dock-bottom.png)
   skin/classic/browser/devtools/dock-side.png               (devtools/dock-side.png)
   skin/classic/browser/devtools/dock-window.png             (devtools/dock-window.png)
   skin/classic/browser/devtools/floating-scrollbars.css     (devtools/floating-scrollbars.css)
 * skin/classic/browser/devtools/inspector.css               (devtools/inspector.css)
-* skin/classic/browser/devtools/toolbox.css                 (devtools/toolbox.css)
+  skin/classic/browser/devtools/toolbox.css                 (devtools/toolbox.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-mobileIcon.png
--- a/browser/themes/winstripe/downloads/downloads-aero.css
+++ b/browser/themes/winstripe/downloads/downloads-aero.css
@@ -2,20 +2,16 @@
  * 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/. */
 
 %define WINSTRIPE_AERO
 %include downloads.css
 %undef WINSTRIPE_AERO
 
 @media (-moz-windows-default-theme) {
-  #downloadsPanel[hasdownloads] > #downloadsFooter {
-    background-color: #f1f5fb;
-  }
-
   richlistitem[type="download"] {
     border: 1px solid transparent;
     border-bottom: 1px solid hsl(213,40%,90%);
   }
 
   richlistitem[type="download"][state="1"]:hover {
     border: 1px solid hsl(213,45%,65%);
     box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset,
--- a/browser/themes/winstripe/downloads/downloads.css
+++ b/browser/themes/winstripe/downloads/downloads.css
@@ -25,18 +25,24 @@
 }
 
 #downloadsHistory > .button-box {
   margin: 1em;
 }
 
 @media (-moz-windows-default-theme) {
   #downloadsPanel[hasdownloads] > #downloadsFooter {
+%ifdef WINSTRIPE_AERO
+    background-color: #f1f5fb;
+%else
     background-color: hsla(216,45%,88%,.98);
+%endif
     box-shadow: 0px 1px 2px rgb(204,214,234) inset;
+    border-bottom-left-radius: 3px;
+    border-bottom-right-radius: 3px;
   }
 }
 
 /*** Downloads Summary and List items ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
   height: 7em;
--- a/config/system-headers
+++ b/config/system-headers
@@ -468,17 +468,16 @@ IOKit/IOMessage.h
 IOKit/pwr_mgt/IOPMLib.h
 iomanip
 iostream
 iostream.h
 iterator
 JavaControl.h
 JavaEmbedding/JavaControl.h
 JavaVM/jni.h
-jemalloc.h
 JManager.h
 JNIEnvTests.h
 jni.h
 #if MOZ_NATIVE_JPEG==1
 jpeglib.h
 #endif
 JVMManagerTests.h
 Kerberos/Kerberos.h
--- a/configure.in
+++ b/configure.in
@@ -7041,26 +7041,99 @@ else
     dnl And we need mozglue symbols to be exported.
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS -rdynamic"
   fi
   if test "$MOZ_LINKER" = 1; then
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS $ZLIB_LIBS"
   fi
 fi
 
+dnl ========================================================
+dnl = Enable dynamic replacement of malloc implementation
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(replace-malloc,
+[  --enable-replace-malloc   Enable ability to dynamically replace the malloc implementation],
+    MOZ_REPLACE_MALLOC=1,
+    MOZ_REPLACE_MALLOC= )
+
+if test -n "$MOZ_REPLACE_MALLOC" -a -z "$MOZ_MEMORY"; then
+    dnl We don't want to enable jemalloc unconditionally because it may be a
+    dnl deliberate choice not to enable it (bug 702250, for instance)
+    AC_MSG_ERROR([--enable-replace-malloc requires --enable-jemalloc])
+elif test -n "$MOZ_REPLACE_MALLOC"; then
+    MOZ_NATIVE_JEMALLOC=
+
+    dnl Replace-malloc Mac linkage quirks
+    if test -n "$MACOSX_DEPLOYMENT_TARGET"; then
+        AC_CACHE_CHECK([how to do weak dynamic linking],
+            ac_cv_weak_dynamic_linking,
+            [echo 'extern void foo() __attribute__((weak_import));int bar() { if (foo) foo(); return 0; }' > conftest.c
+             if AC_TRY_COMMAND([${CC-cc} -o conftest${DLL_SUFFIX} $CFLAGS -dynamiclib $LDFLAGS -Wl,-U,_foo conftest.c $LIBS 1>&5]) &&
+                test -s conftest${DLL_SUFFIX}; then
+                 dnl There are several ways the linker can put link edit rules in a binary:
+                 dnl - classic info only (for OSX < 10.6)
+                 dnl - dyld info only
+                 dnl - both
+                 if otool -l conftest${DLL_SUFFIX} 2> /dev/null | grep "LC_DYLD_INFO_ONLY" > /dev/null; then
+                     _CLASSIC_INFO=
+                 else
+                     _CLASSIC_INFO=1
+                 fi
+                 if otool -l conftest${DLL_SUFFIX} 2> /dev/null | grep "LC_DYLD_INFO" > /dev/null; then
+                     _DYLD_INFO=1
+                 else
+                     _DYLD_INFO=
+                 fi
+                 dnl With classic info, we need to build with -flat_namespace.
+                 dnl With dyld info, Xcode 4.5 does the right thing without additional flags,
+                 dnl but Xcode < 4.5 requires a dummy library and -flat_namespace because it
+                 dnl forgets to set the weak flag in the dyld info.
+                 dnl See http://glandium.org/blog/?p=2764 for more details.
+                 dnl
+                 dnl Values for ac_cv_weak_dynamic_linking, and subsequently
+                 dnl MOZ_REPLACE_MALLOC_LINKAGE are thus:
+                 dnl - "flat namespace" when -flat_namespace alone is needed
+                 dnl - "dummy library" when a dummy library and -flat_namespace are needed
+                 dnl - "compiler support" when nothing is needed
+                 if test -n "$_DYLD_INFO" && dyldinfo -bind conftest${DLL_SUFFIX} 2> /dev/null | grep "_foo (weak import)" > /dev/null; then
+                     if test -n "$_CLASSIC_INFO"; then
+                         ac_cv_weak_dynamic_linking="flat namespace"
+                     else
+                         ac_cv_weak_dynamic_linking="compiler support"
+                     fi
+                 else
+                     if test -n "$_DYLD_INFO"; then
+                         ac_cv_weak_dynamic_linking="dummy library"
+                     else
+                         ac_cv_weak_dynamic_linking="flat namespace"
+                     fi
+                 fi
+             else
+                 AC_ERROR([couldn't compile a simple C file])
+             fi
+             rm -rf conftest*])
+        MOZ_REPLACE_MALLOC_LINKAGE="$ac_cv_weak_dynamic_linking"
+    fi
+fi
+AC_SUBST(MOZ_REPLACE_MALLOC)
+AC_SUBST(MOZ_REPLACE_MALLOC_LINKAGE)
+
+dnl ========================================================
+dnl = Jemalloc build setup
+dnl ========================================================
 if test -z "$MOZ_MEMORY"; then
-  if test -n "$MOZ_JEMALLOC"; then
+  if test -n "$MOZ_JEMALLOC3" -a -z "$MOZ_REPLACE_MALLOC"; then
     MOZ_NATIVE_JEMALLOC=1
     AC_CHECK_FUNCS(mallctl nallocm,,
       [MOZ_NATIVE_JEMALLOC=
        break])
     if test -n "$MOZ_NATIVE_JEMALLOC"; then
       MOZ_MEMORY=1
       AC_DEFINE(MOZ_MEMORY)
-      AC_DEFINE(MOZ_JEMALLOC)
+      AC_DEFINE(MOZ_JEMALLOC3)
       AC_DEFINE(MOZ_NATIVE_JEMALLOC)
     fi
   fi
   case "${target}" in
     *-mingw*)
       if test -z "$WIN32_REDIST_DIR" -a -z "$MOZ_DEBUG"; then
         AC_MSG_WARN([When not building jemalloc, you need to set WIN32_REDIST_DIR to the path to the Visual C++ Redist (usually VCINSTALLDIR\redist\x86\Microsoft.VC80.CRT, for VC++ v8) if you intend to distribute your build.])
       fi
@@ -7085,18 +7158,18 @@ else
       ;;
     *)
       AC_MSG_ERROR([Unexpected pointer size])
       ;;
     esac
   fi
 
   AC_DEFINE(MOZ_MEMORY)
-  if test -n "$MOZ_JEMALLOC"; then
-    AC_DEFINE(MOZ_JEMALLOC)
+  if test -n "$MOZ_JEMALLOC3"; then
+    AC_DEFINE(MOZ_JEMALLOC3)
   fi
   if test "x$MOZ_DEBUG" = "x1"; then
     AC_DEFINE(MOZ_MEMORY_DEBUG)
   fi
   dnl The generic feature tests that determine how to compute ncpus are long and
   dnl complicated.  Therefore, simply define special cpp variables for the
   dnl platforms we have special knowledge of.
   case "${target}" in
@@ -7147,17 +7220,17 @@ else
     export DLLFLAGS
     ;;
   *)
     AC_MSG_ERROR([--enable-jemalloc not supported on ${target}])
     ;;
   esac
 fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
-AC_SUBST(MOZ_JEMALLOC)
+AC_SUBST(MOZ_JEMALLOC3)
 AC_SUBST(MOZ_NATIVE_JEMALLOC)
 AC_SUBST(MOZ_GLUE_LDFLAGS)
 AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS)
 AC_SUBST(WIN32_CRT_LIBS)
 dnl Need to set this for make because NSS doesn't have configure
 AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
@@ -8568,18 +8641,29 @@ if test -n "$LIBXUL_SDK"; then
     MOZ_APP_STATIC_INI=
 fi
 AC_SUBST(MOZ_APP_STATIC_INI)
 
 AC_SUBST(MOZ_PKG_SPECIAL)
 
 AC_SUBST(MOZILLA_OFFICIAL)
 
+AC_DEFINE_UNQUOTED(MOZ_TELEMETRY_DISPLAY_REV, 2)
+AC_SUBST(MOZ_TELEMETRY_DISPLAY_REV)
+
 if test "$MOZ_TELEMETRY_REPORTING"; then
     AC_DEFINE(MOZ_TELEMETRY_REPORTING)
+    # Those lines will remain commented until we are ready to enable
+    # telemetry by default on Nightly & Aurora channels.
+    #
+    # Enable Telemetry by default for nightly and aurora channels
+    # if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \
+    #     "$MOZ_UPDATE_CHANNEL" = "aurora"; then
+    #     AC_DEFINE(MOZ_TELEMETRY_ON_BY_DEFAULT)
+    # fi
 fi
 
 dnl win32 options
 AC_SUBST(MOZ_MAPINFO)
 AC_SUBST(MOZ_BROWSE_INFO)
 AC_SUBST(MOZ_TOOLS_DIR)
 AC_SUBST(WIN32_REDIST_DIR)
 AC_SUBST(MAKENSISU)
@@ -8996,35 +9080,51 @@ MOZ_PER_WINDOW_PRIVATE_BROWSING=${MOZ_PE
 if cmp -s ./mozinfo.json.tmp ./mozinfo.json; then
   rm ./mozinfo.json.tmp
 else
   mv -f ./mozinfo.json.tmp ./mozinfo.json
 fi
 
 # Run jemalloc configure script
 
-if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_JEMALLOC" -a "$MOZ_MEMORY" ; then
+if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
   ac_configure_args="$_SUBDIR_CONFIG_ARGS --build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_"
-  case "$OS_ARCH" in
-    Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
-      MANGLE="malloc calloc valloc free realloc posix_memalign"
-      case "$OS_ARCH" in
-        Linux)
-          MANGLE="$MANGLE memalign malloc_usable_size"
-          ;;
-        FreeBSD)
-          MANGLE="$MANGLE malloc_usable_size"
-          ;;
-      esac
-      ;;
-  esac
+  if test -n "$MOZ_REPLACE_MALLOC"; then
+    # When using replace_malloc, we always want memalign and valloc exported from jemalloc.
+    ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
+    ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes"
+  fi
+  if test -n "$MOZ_JEMALLOC3"; then
+    case "${OS_ARCH}" in
+      WINNT|Darwin)
+        # We want jemalloc functions to be kept hidden on both Mac and Windows
+        # See memory/build/mozmemory_wrap.h for details.
+        ac_configure_args="$ac_configure_args --without-export"
+        ;;
+    esac
+  elif test "${OS_ARCH}" = Darwin; then
+    # When building as a replace-malloc lib, disabling the zone allocator
+    # forces to use pthread_atfork.
+    ac_configure_args="$ac_configure_args --disable-zone-allocator"
+  fi
+  _MANGLE="malloc posix_memalign aligned_alloc calloc realloc free memalign valloc malloc_usable_size"
+  JEMALLOC_WRAPPER=
+  if test -z "$MOZ_REPLACE_MALLOC"; then
+    case "$OS_ARCH" in
+      Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
+        MANGLE=$_MANGLE
+        ;;
+    esac
+  elif test -z "$MOZ_JEMALLOC3"; then
+    MANGLE=$_MANGLE
+    JEMALLOC_WRAPPER=replace_
+  fi
   if test -n "$MANGLE"; then
     MANGLED=
-    JEMALLOC_WRAPPER=
-    if test -n "$_WRAP_MALLOC"; then
+    if test -n "$_WRAP_MALLOC" -a -z "$JEMALLOC_WRAPPER"; then
       JEMALLOC_WRAPPER=__wrap_
     fi
     for mangle in ${MANGLE}; do
       if test -n "$MANGLED"; then
         MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle,$MANGLED"
       else
         MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle"
       fi
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -22,20 +22,19 @@ class nsIFrame;
 
 namespace mozilla {
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
-  eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
-  eLinkState_NotLink    = 3
+  eLinkState_NotLink    = 3 
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID \
 { 0xe2985850, 0x81ca, 0x4b5d, \
   { 0xb0, 0xf3, 0xe3, 0x95, 0xd5, 0x0d, 0x85, 0x64 } }
 
 /**
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -16,17 +16,17 @@ interface nsIURI;
 #include "nsNPAPIPluginInstance.h"
 %}
 [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
 
 /**
  * This interface represents a content node that loads objects.
  */
 
-[scriptable, uuid(a812424b-4820-4e28-96c8-dd2b69e36496)]
+[scriptable, uuid(649b8a75-8623-437f-ad30-0ac596c8452e)]
 interface nsIObjectLoadingContent : nsISupports
 {
   /**
    * See notes in nsObjectLoadingContent.h
    */
   const unsigned long TYPE_LOADING  = 0;
   const unsigned long TYPE_IMAGE    = 1;
   const unsigned long TYPE_PLUGIN   = 2;
@@ -126,16 +126,26 @@ interface nsIObjectLoadingContent : nsIS
   readonly attribute boolean activated;
 
   [noscript] void stopPluginInstance();
 
   [noscript] void syncStartPluginInstance();
   [noscript] void asyncStartPluginInstance();
 
   /**
+   * Requests the plugin instance for scripting, attempting to spawn it if
+   * appropriate.
+   *
+   * The first time content js tries to access a pre-empted plugin
+   * (click-to-play or play preview), an event is dispatched.
+   */
+  [noscript] nsNPAPIPluginInstancePtr
+    scriptRequestPluginInstance(in bool callerIsContentJS);
+
+  /**
    * The URL of the data/src loaded in the object. This may be null (i.e.
    * an <embed> with no src).
    */
   readonly attribute nsIURI srcURI;
 
   readonly attribute unsigned long pluginFallbackType;
 
   /**
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -19,34 +19,40 @@
 #include "mozilla/Services.h"
 
 namespace mozilla {
 namespace dom {
 
 Link::Link(Element *aElement)
   : mElement(aElement)
   , mHistory(services::GetHistoryService())
-  , mLinkState(defaultState)
+  , mLinkState(eLinkState_NotLink)
+  , mNeedsRegistration(false)
   , mRegistered(false)
 {
   NS_ABORT_IF_FALSE(mElement, "Must have an element");
 }
 
 Link::~Link()
 {
   UnregisterFromHistory();
 }
 
+bool
+Link::ElementHasHref() const
+{
+  return ((!mElement->IsSVG() && mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href))
+        || (!mElement->IsHTML() && mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)));
+}
+
 nsLinkState
 Link::GetLinkState() const
 {
   NS_ASSERTION(mRegistered,
                "Getting the link state of an unregistered Link!");
-  NS_ASSERTION(mLinkState != eLinkState_Unknown,
-               "Getting the link state with an unknown value!");
   return nsLinkState(mLinkState);
 }
 
 void
 Link::SetLinkState(nsLinkState aState)
 {
   NS_ASSERTION(mRegistered,
                "Setting the link state of an unregistered Link!");
@@ -69,46 +75,38 @@ Link::SetLinkState(nsLinkState aState)
 
 nsEventStates
 Link::LinkState() const
 {
   // We are a constant method, but we are just lazily doing things and have to
   // track that state.  Cast away that constness!
   Link *self = const_cast<Link *>(this);
 
-  // If we are not in the document, default to not visited.
   Element *element = self->mElement;
-  if (!element->IsInDoc()) {
-    self->mLinkState = eLinkState_Unvisited;
-  }
 
-  // If we have not yet registered for notifications and are in an unknown
-  // state, register now!
-  if (!mRegistered && mLinkState == eLinkState_Unknown) {
-    // First, make sure the href attribute has a valid link (bug 23209).
+  // If we have not yet registered for notifications and need to,
+  // due to our href changing, register now!
+  if (!mRegistered && mNeedsRegistration && element->IsInDoc()) {
+    // Only try and register once.
+    self->mNeedsRegistration = false;
+
     nsCOMPtr<nsIURI> hrefURI(GetURI());
-    if (!hrefURI) {
-      self->mLinkState = eLinkState_NotLink;
-      return nsEventStates();
-    }
 
     // Assume that we are not visited until we are told otherwise.
     self->mLinkState = eLinkState_Unvisited;
 
-    // We have a good href, so register with History.
-    if (mHistory) {
+    // Make sure the href attribute has a valid link (bug 23209).
+    // If we have a good href, register with History if available.
+    if (mHistory && hrefURI) {
       nsresult rv = mHistory->RegisterVisitedCallback(hrefURI, self);
       if (NS_SUCCEEDED(rv)) {
         self->mRegistered = true;
 
         // And make sure we are in the document's link map.
-        nsIDocument *doc = element->GetCurrentDoc();
-        if (doc) {
-          doc->AddStyleRelevantLink(self);
-        }
+        element->GetCurrentDoc()->AddStyleRelevantLink(self);
       }
     }
   }
 
   // Otherwise, return our known state.
   if (mLinkState == eLinkState_Visited) {
     return NS_EVENT_STATE_VISITED;
   }
@@ -130,18 +128,18 @@ Link::GetURI() const
     return uri.forget();
   }
 
   // Otherwise obtain it.
   Link *self = const_cast<Link *>(this);
   Element *element = self->mElement;
   uri = element->GetHrefURI();
 
-  // We want to cache the URI if the node is in the document.
-  if (uri && element->IsInDoc()) {
+  // We want to cache the URI if we have it
+  if (uri) {
     mCachedURI = uri;
   }
 
   return uri.forget();
 }
 
 nsresult
 Link::SetProtocol(const nsAString &aProtocol)
@@ -419,49 +417,66 @@ Link::GetHash(nsAString &_hash)
     NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
     _hash.Assign(PRUnichar('#'));
     AppendUTF8toUTF16(ref, _hash);
   }
   return NS_OK;
 }
 
 void
-Link::ResetLinkState(bool aNotify)
+Link::ResetLinkState(bool aNotify, bool aHasHref)
 {
-  // If we are in our default state, bail early.
-  if (mLinkState == defaultState) {
-    return;
+  nsLinkState defaultState;
+
+  // The default state for links with an href is unvisited.
+  if (aHasHref) {
+    defaultState = eLinkState_Unvisited;
+  } else {
+    defaultState = eLinkState_NotLink;
   }
 
-  Element *element = mElement;
+  // If !mNeedsRegstration, then either we've never registered, or we're
+  // currently registered; in either case, we should remove ourself
+  // from the doc and the history.
+  if (!mNeedsRegistration && mLinkState != eLinkState_NotLink) {
+    nsIDocument *doc = mElement->GetCurrentDoc();
+    if (doc && (mRegistered || mLinkState == eLinkState_Visited)) {
+      // Tell the document to forget about this link if we've registered
+      // with it before.
+      doc->ForgetLink(this);
+    }
 
-  // Tell the document to forget about this link if we were a link before.
-  nsIDocument *doc = element->GetCurrentDoc();
-  if (doc && mLinkState != eLinkState_NotLink) {
-    doc->ForgetLink(this);
+    UnregisterFromHistory();
   }
 
-  UnregisterFromHistory();
+  // If we have an href, we should register with the history.
+  mNeedsRegistration = aHasHref;
+
+  // If we've cached the URI, reset always invalidates it.
+  mCachedURI = nullptr;
 
   // Update our state back to the default.
   mLinkState = defaultState;
 
-  // Get rid of our cached URI.
-  mCachedURI = nullptr;
-
   // We have to be very careful here: if aNotify is false we do NOT
   // want to call UpdateState, because that will call into LinkState()
   // and try to start off loads, etc.  But ResetLinkState is called
   // with aNotify false when things are in inconsistent states, so
   // we'll get confused in that situation.  Instead, just silently
-  // update the link state on mElement.
+  // update the link state on mElement. Since we might have set the
+  // link state to unvisited, make sure to update with that state if
+  // required.
   if (aNotify) {
     mElement->UpdateState(aNotify);
   } else {
-    mElement->UpdateLinkState(nsEventStates());
+    if (mLinkState == eLinkState_Unvisited) {
+      mElement->UpdateLinkState(NS_EVENT_STATE_UNVISITED);
+    } else {
+      mElement->UpdateLinkState(nsEventStates());
+    }
   }
 }
 
 void
 Link::UnregisterFromHistory()
 {
   // If we are not registered, we have nothing to do.
   if (!mRegistered) {
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -23,18 +23,16 @@ class Element;
   { 0x7EA57721, 0xE373, 0x458E, \
     {0x8F, 0x44, 0xF8, 0x96, 0x56, 0xB4, 0x14, 0xF5 } }
 
 class Link : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
-  static const nsLinkState defaultState = eLinkState_Unknown;
-
   /**
    * aElement is the element pointer corresponding to this link.
    */
   Link(Element* aElement);
   nsLinkState GetLinkState() const;
   virtual void SetLinkState(nsLinkState aState);
 
   /**
@@ -72,17 +70,17 @@ public:
 
   /**
    * Invalidates any link caching, and resets the state to the default.
    *
    * @param aNotify
    *        true if ResetLinkState should notify the owning document about style
    *        changes or false if it should not.
    */
-  void ResetLinkState(bool aNotify);
+  void ResetLinkState(bool aNotify, bool aHasHref);
   
   // This method nevers returns a null element.
   Element* GetElement() const { return mElement; }
 
   /**
    * DNS prefetch has been deferred until later, e.g. page load complete.
    */
   virtual void OnDNSPrefetchDeferred() { /*do nothing*/ }
@@ -98,16 +96,18 @@ public:
    * @returns boolean
    *          Defaults to true; should be overridden for specialised cases
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t
     SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
+  bool ElementHasHref() const;
+
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
   {
@@ -136,16 +136,18 @@ private:
   Element * const mElement;
 
   // Strong reference to History.  The link has to unregister before History
   // can disappear.
   nsCOMPtr<IHistory> mHistory;
 
   uint16_t mLinkState;
 
+  bool mNeedsRegistration;
+
   bool mRegistered;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -7264,17 +7264,17 @@ nsDocument::RefreshLinkHrefs()
   // remove them from the document, so we need a copy of what is in the
   // hashtable.
   LinkArray linksToNotify(mStyledLinks.Count());
   (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
 
   // Reset all of our styled links.
   nsAutoScriptBlocker scriptBlocker;
   for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
-    linksToNotify[i]->ResetLinkState(true);
+    linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
   }
 }
 
 nsresult
 nsDocument::CloneDocHelper(nsDocument* clone) const
 {
   clone->mIsStaticDocument = mCreatingStaticClone;
 
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -782,31 +782,40 @@ nsFrameLoader::Show(int32_t marginWidth,
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return false;
   }
 
   if (!mRemoteFrame) {
     if (!mDocShell)
       return false;
-    nsCOMPtr<nsIPresShell> presShell;
-    mDocShell->GetPresShell(getter_AddRefs(presShell));
-    if (presShell)
-      return true;
 
     mDocShell->SetMarginWidth(marginWidth);
     mDocShell->SetMarginHeight(marginHeight);
 
     nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
     if (sc) {
       sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
                                          scrollbarPrefX);
       sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
                                          scrollbarPrefY);
     }
+
+    nsCOMPtr<nsIPresShell> presShell;
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+    if (presShell) {
+      // Ensure root scroll frame is reflowed in case scroll preferences or
+      // margins have changed
+      nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+      if (rootScrollFrame) {
+        presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
+                                    NS_FRAME_IS_DIRTY);
+      }
+      return true;
+    }
   }
 
   nsIView* view = frame->EnsureInnerView();
   if (!view)
     return false;
 
   if (mRemoteFrame) {
     return ShowRemoteFrame(GetSubDocumentSize(frame));
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -158,37 +158,41 @@ InDocCheckEvent::Run()
     nsObjectLoadingContent *objLC =
       static_cast<nsObjectLoadingContent *>(mContent.get());
     objLC->UnloadObject();
   }
   return NS_OK;
 }
 
 /**
- * A task for firing PluginNotFound and PluginBlocklisted DOM Events.
+ * Helper task for firing simple events
  */
-class nsPluginOutdatedEvent : public nsRunnable {
+class nsSimplePluginEvent : public nsRunnable {
 public:
-  nsPluginOutdatedEvent(nsIContent* aContent) : mContent(aContent) {}
+  nsSimplePluginEvent(nsIContent* aContent, const nsAString &aEvent)
+    : mContent(aContent),
+      mEvent(aEvent)
+  {}
 
-  ~nsPluginOutdatedEvent() {}
+  ~nsSimplePluginEvent() {}
 
   NS_IMETHOD Run();
 
 private:
   nsCOMPtr<nsIContent> mContent;
+  nsString mEvent;
 };
 
 NS_IMETHODIMP
-nsPluginOutdatedEvent::Run()
+nsSimplePluginEvent::Run()
 {
-  LOG(("OBJLC [%p]: nsPluginOutdatedEvent firing", mContent.get()));
+  LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mContent.get(),
+       mEvent.get()));
   nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
-                                       NS_LITERAL_STRING("PluginOutdated"),
-                                       true, true);
+                                       mEvent, true, true);
   return NS_OK;
 }
 
 /**
  * A task for firing PluginCrashed DOM Events.
  */
 class nsPluginCrashedEvent : public nsRunnable {
 public:
@@ -626,16 +630,17 @@ nsObjectLoadingContent::nsObjectLoadingC
   , mFallbackType(eFallbackAlternate)
   , mChannelLoaded(false)
   , mInstantiating(false)
   , mNetworkCreated(true)
   , mActivated(false)
   , mPlayPreviewCanceled(false)
   , mIsStopping(false)
   , mIsLoading(false)
+  , mScriptRequested(false)
   , mSrcStreamLoading(false) {}
 
 nsObjectLoadingContent::~nsObjectLoadingContent()
 {
   // Should have been unbound from the tree at this point, and InDocCheckEvent
   // keeps us alive
   if (mFrameLoader) {
     NS_NOTREACHED("Should not be tearing down frame loaders at this point");
@@ -743,22 +748,23 @@ nsObjectLoadingContent::InstantiatePlugi
     nsCOMPtr<nsIBlocklistService> blocklist =
       do_GetService("@mozilla.org/extensions/blocklist;1");
     if (blocklist) {
       uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
       blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
                                          EmptyString(), &blockState);
       if (blockState == nsIBlocklistService::STATE_OUTDATED) {
         // Fire plugin outdated event if necessary
-        LOG(("OBJLC [%p]: Dispatching nsPluginOutdatedEvent for content %p\n",
+        LOG(("OBJLC [%p]: Dispatching plugin outdated event for content %p\n",
              this));
-        nsCOMPtr<nsIRunnable> ev = new nsPluginOutdatedEvent(thisContent);
+        nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
+                                                     NS_LITERAL_STRING("PluginOutdated"));
         nsresult rv = NS_DispatchToCurrentThread(ev);
         if (NS_FAILED(rv)) {
-          NS_WARNING("failed to dispatch nsPluginOutdatedEvent");
+          NS_WARNING("failed to dispatch nsSimplePluginEvent");
         }
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -2008,16 +2014,18 @@ nsObjectLoadingContent::UnloadObject(boo
     }
     mChannelLoaded = false;
     mType = eType_Loading;
     mURI = mOriginalURI = mBaseURI = nullptr;
     mContentType.Truncate();
     mOriginalContentType.Truncate();
   }
 
+  mScriptRequested = false;
+
   // This call should be last as it may re-enter
   StopPluginInstance();
 }
 
 void
 nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
                                            nsEventStates aOldState,
                                            bool aSync,
@@ -2179,16 +2187,55 @@ nsObjectLoadingContent::PluginCrashed(ns
   nsresult rv = NS_DispatchToCurrentThread(ev);
   if (NS_FAILED(rv)) {
     NS_WARNING("failed to dispatch nsPluginCrashedEvent");
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsObjectLoadingContent::ScriptRequestPluginInstance(bool aCallerIsContentJS,
+                                                    nsNPAPIPluginInstance **aResult)
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+
+  *aResult = nullptr;
+
+  // The first time content script attempts to access placeholder content, fire
+  // an event.  Fallback types >= eFallbackClickToPlay are plugin-replacement
+  // types, see header.
+  if (aCallerIsContentJS && !mScriptRequested &&
+      InActiveDocument(thisContent) && mType == eType_Null &&
+      mFallbackType >= eFallbackClickToPlay) {
+    nsCOMPtr<nsIRunnable> ev =
+      new nsSimplePluginEvent(thisContent,
+                              NS_LITERAL_STRING("PluginScripted"));
+    nsresult rv = NS_DispatchToCurrentThread(ev);
+    if (NS_FAILED(rv)) {
+      NS_NOTREACHED("failed to dispatch PluginScripted event");
+    }
+    mScriptRequested = true;
+  } else if (mType == eType_Plugin && !mInstanceOwner &&
+             nsContentUtils::IsSafeToRunScript() &&
+             InActiveDocument(thisContent)) {
+    // If we're configured as a plugin in an active document and it's safe to
+    // run scripts right now, try spawning synchronously
+    SyncStartPluginInstance();
+  }
+
+  if (mInstanceOwner) {
+    return mInstanceOwner->GetInstance(aResult);
+  }
+
+  // Note that returning a null plugin is expected (and happens often)
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsObjectLoadingContent::SyncStartPluginInstance()
 {
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Must be able to run script in order to instantiate a plugin instance!");
 
   // Don't even attempt to start an instance unless the content is in
   // the document and active
   nsCOMPtr<nsIContent> thisContent =
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -432,22 +432,26 @@ class nsObjectLoadingContent : public ns
     bool                        mPlayPreviewCanceled : 1;
 
     // Protects DoStopPlugin from reentry (bug 724781).
     bool                        mIsStopping : 1;
 
     // Protects LoadObject from re-entry
     bool                        mIsLoading : 1;
 
+    // For plugin stand-in types (click-to-play, play preview, ...) tracks
+    // whether content js has tried to access the plugin script object.
+    bool                        mScriptRequested : 1;
+
     // Used to track when we might try to instantiate a plugin instance based on
     // a src data stream being delivered to this object. When this is true we
     // don't want plugin instance instantiation code to attempt to load src data
     // again or we'll deliver duplicate streams. Should be cleared when we are
     // not loading src data.
-    bool mSrcStreamLoading;
+    bool                        mSrcStreamLoading : 1;
 
 
     nsWeakFrame                 mPrintFrame;
 
     nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
 };
 
 #endif
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -53,19 +53,28 @@ using namespace mozilla::gl;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 NS_IMETHODIMP
 WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
                                      const char* aTopic,
                                      const PRUnichar* aSomeData)
 {
-  if (strcmp(aTopic, "memory-pressure") == 0)
-    mContext->ForceLoseContext();
-  return NS_OK;
+    if (strcmp(aTopic, "memory-pressure"))
+        return NS_OK;
+
+    bool wantToLoseContext = true;
+
+    if (!nsCRT::strcmp(aSomeData, NS_LITERAL_STRING("heap-minimize").get()))
+        wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
+
+    if (wantToLoseContext)
+        mContext->ForceLoseContext();
+
+    return NS_OK;
 }
 
 
 nsresult NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult);
 
 nsresult
 NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult)
 {
@@ -163,16 +172,17 @@ WebGLContext::WebGLContext()
     WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
 
     mAllowRestore = true;
     mContextLossTimerRunning = false;
     mDrawSinceContextLossTimerSet = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextStable;
     mContextLostErrorSet = false;
+    mLoseContextOnHeapMinimize = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
 
     mLastUseIndex = 0;
 
     mMinInUseAttribArrayLengthCached = false;
     mMinInUseAttribArrayLength = 0;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -5,22 +5,24 @@
 
 #ifndef WEBGLCONTEXT_H_
 #define WEBGLCONTEXT_H_
 
 #include "WebGLElementArrayCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLShader.h"
 #include "WebGLBuffer.h"
+#include "WebGLProgram.h"
+#include "WebGLUniformLocation.h"
+#include "WebGLFramebuffer.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShaderPrecisionFormat.h"
 #include <stdarg.h>
-#include <vector>
 
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsCycleCollectionNoteChild.h"
 
 #include "nsIDocShell.h"
 
@@ -65,19 +67,16 @@
 #define MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS    0     // Page 164
 #define MINVALUE_GL_MAX_RENDERBUFFER_SIZE             1024  // Different from the spec, which sets it to 1 on page 164
 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS  8     // Page 164
 
 class nsIPropertyBag;
 
 namespace mozilla {
 
-class WebGLProgram;
-class WebGLFramebuffer;
-class WebGLUniformLocation;
 class WebGLMemoryPressureObserver;
 class WebGLContextBoundObject;
 class WebGLActiveInfo;
 class WebGLExtensionBase;
 
 namespace dom {
 struct WebGLContextAttributes;
 struct WebGLContextAttributesInitializer;
@@ -838,16 +837,17 @@ protected:
 
     bool mInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mMinCapability;
     bool mDisableExtensions;
     bool mHasRobustness;
     bool mIsMesa;
+    bool mLoseContextOnHeapMinimize;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     WebGLuint mActiveTexture;
     WebGLenum mWebGLError;
 
     // whether shader validation is supported
@@ -1154,843 +1154,16 @@ public:
 
 // used by DOM bindings in conjunction with GetParentObject
 inline nsISupports*
 ToSupports(WebGLContext* context)
 {
   return static_cast<nsICanvasRenderingContextInternal*>(context);
 }
 
-/** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart
-  * 
-  * \param string input/output: the string to split, becomes the string without the bracket part
-  * \param bracketPart output: gets the bracket part.
-  * 
-  * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split.
-  */
-static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart)
-{
-    MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string");
-
-    if (string.IsEmpty())
-        return false;
-
-    char *string_start = string.BeginWriting();
-    char *s = string_start + string.Length() - 1;
-
-    if (*s != ']')
-        return false;
-
-    while (*s != '[' && s != string_start)
-        s--;
-
-    if (*s != '[')
-        return false;
-
-    bracketPart.Assign(s);
-    *s = 0;
-    string.EndWriting();
-    string.SetLength(s - string_start);
-    return true;
-}
-
-typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap;
-typedef nsDataHashtable<nsCStringHashKey, WebGLUniformInfo> CStringToUniformInfoMap;
-
-class WebGLProgram MOZ_FINAL
-    : public nsISupports
-    , public WebGLRefCountedObject<WebGLProgram>
-    , public LinkedListElement<WebGLProgram>
-    , public WebGLContextBoundObject
-    , public nsWrapperCache
-{
-public:
-    WebGLProgram(WebGLContext *context)
-        : WebGLContextBoundObject(context)
-        , mLinkStatus(false)
-        , mGeneration(0)
-        , mAttribMaxNameLength(0)
-    {
-        SetIsDOMBinding();
-        mContext->MakeContextCurrent();
-        mGLName = mContext->gl->fCreateProgram();
-        mContext->mPrograms.insertBack(this);
-    }
-
-    ~WebGLProgram() {
-        DeleteOnce();
-    }
-
-    void Delete() {
-        DetachShaders();
-        mContext->MakeContextCurrent();
-        mContext->gl->fDeleteProgram(mGLName);
-        LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
-    }
-
-    void DetachShaders() {
-        mAttachedShaders.Clear();
-    }
-
-    WebGLuint GLName() { return mGLName; }
-    const nsTArray<WebGLRefPtr<WebGLShader> >& AttachedShaders() const { return mAttachedShaders; }
-    bool LinkStatus() { return mLinkStatus; }
-    uint32_t Generation() const { return mGeneration.value(); }
-    void SetLinkStatus(bool val) { mLinkStatus = val; }
-
-    bool ContainsShader(WebGLShader *shader) {
-        return mAttachedShaders.Contains(shader);
-    }
-
-    // return true if the shader wasn't already attached
-    bool AttachShader(WebGLShader *shader) {
-        if (ContainsShader(shader))
-            return false;
-        mAttachedShaders.AppendElement(shader);
-
-        mContext->MakeContextCurrent();
-        mContext->gl->fAttachShader(GLName(), shader->GLName());
-
-        return true;
-    }
-
-    // return true if the shader was found and removed
-    bool DetachShader(WebGLShader *shader) {
-        if (!mAttachedShaders.RemoveElement(shader))
-            return false;
-
-        mContext->MakeContextCurrent();
-        mContext->gl->fDetachShader(GLName(), shader->GLName());
-
-        return true;
-    }
-
-    bool HasAttachedShaderOfType(GLenum shaderType) {
-        for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
-            if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool HasBothShaderTypesAttached() {
-        return
-            HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) &&
-            HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER);
-    }
-
-    bool HasBadShaderAttached() {
-        for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
-            if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    size_t UpperBoundNumSamplerUniforms() {
-        size_t numSamplerUniforms = 0;
-        for (size_t i = 0; i < mAttachedShaders.Length(); ++i) {
-            const WebGLShader *shader = mAttachedShaders[i];
-            if (!shader)
-                continue;
-            for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) {
-                WebGLUniformInfo u = shader->mUniformInfos[j];
-                if (u.type == SH_SAMPLER_2D ||
-                    u.type == SH_SAMPLER_CUBE)
-                {
-                    numSamplerUniforms += u.arraySize;
-                }
-            }
-        }
-        return numSamplerUniforms;
-    }
-
-    bool NextGeneration()
-    {
-        if (!(mGeneration + 1).isValid())
-            return false; // must exit without changing mGeneration
-        ++mGeneration;
-        return true;
-    }
-
-    /* Called only after LinkProgram */
-    bool UpdateInfo();
-
-    /* Getters for cached program info */
-    bool IsAttribInUse(unsigned i) const { return mAttribsInUse[i]; }
-
-    /* Maps identifier |name| to the mapped identifier |*mappedName|
-     * Both are ASCII strings.
-     */
-    void MapIdentifier(const nsACString& name, nsCString *mappedName) {
-        if (!mIdentifierMap) {
-            // if the identifier map doesn't exist yet, build it now
-            mIdentifierMap = new CStringMap;
-            mIdentifierMap->Init();
-            for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
-                for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
-                    const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
-                    mIdentifierMap->Put(attrib.original, attrib.mapped);
-                }
-                for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
-                    const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
-                    mIdentifierMap->Put(uniform.original, uniform.mapped);
-                }
-            }
-        }
-
-        nsCString mutableName(name);
-        nsCString bracketPart;
-        bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-        if (hadBracketPart)
-            mutableName.AppendLiteral("[0]");
-
-        if (mIdentifierMap->Get(mutableName, mappedName)) {
-            if (hadBracketPart) {
-                nsCString mappedBracketPart;
-                bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart);
-                if (mappedHadBracketPart)
-                    mappedName->Append(bracketPart);
-            }
-            return;
-        }
-
-        // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform
-        // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0].
-        mutableName.AppendLiteral("[0]");
-        if (mIdentifierMap->Get(mutableName, mappedName))
-            return;
-
-        // not found? return name unchanged. This case happens e.g. on bad user input, or when
-        // we're not using identifier mapping, or if we didn't store an identifier in the map because
-        // e.g. its mapping is trivial (as happens for short identifiers)
-        mappedName->Assign(name);
-    }
-
-    /* Un-maps mapped identifier |name| to the original identifier |*reverseMappedName|
-     * Both are ASCII strings.
-     */
-    void ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) {
-        if (!mIdentifierReverseMap) {
-            // if the identifier reverse map doesn't exist yet, build it now
-            mIdentifierReverseMap = new CStringMap;
-            mIdentifierReverseMap->Init();
-            for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
-                for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
-                    const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
-                    mIdentifierReverseMap->Put(attrib.mapped, attrib.original);
-                }
-                for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
-                    const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
-                    mIdentifierReverseMap->Put(uniform.mapped, uniform.original);
-                }
-            }
-        }
-
-        nsCString mutableName(name);
-        nsCString bracketPart;
-        bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-        if (hadBracketPart)
-            mutableName.AppendLiteral("[0]");
-
-        if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) {
-            if (hadBracketPart) {
-                nsCString reverseMappedBracketPart;
-                bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart);
-                if (reverseMappedHadBracketPart)
-                    reverseMappedName->Append(bracketPart);
-            }
-            return;
-        }
-
-        // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform
-        // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0].
-        mutableName.AppendLiteral("[0]");
-        if (mIdentifierReverseMap->Get(mutableName, reverseMappedName))
-            return;
-
-        // not found? return name unchanged. This case happens e.g. on bad user input, or when
-        // we're not using identifier mapping, or if we didn't store an identifier in the map because
-        // e.g. its mapping is trivial (as happens for short identifiers)
-        reverseMappedName->Assign(name);
-    }
-
-    /* Returns the uniform array size (or 1 if the uniform is not an array) of
-     * the uniform with given mapped identifier.
-     *
-     * Note: the input string |name| is the mapped identifier, not the original identifier.
-     */
-    WebGLUniformInfo GetUniformInfoForMappedIdentifier(const nsACString& name) {
-        if (!mUniformInfoMap) {
-            // if the identifier-to-array-size map doesn't exist yet, build it now
-            mUniformInfoMap = new CStringToUniformInfoMap;
-            mUniformInfoMap->Init();
-            for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
-                for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
-                    const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
-                    const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
-                    mUniformInfoMap->Put(uniform.mapped, info);
-                }
-            }
-        }
-
-        nsCString mutableName(name);
-        nsCString bracketPart;
-        bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
-        // if there is a bracket, we're either an array or an entry in an array.
-        if (hadBracketPart)
-            mutableName.AppendLiteral("[0]");
-
-        WebGLUniformInfo info;
-        mUniformInfoMap->Get(mutableName, &info);
-        // we don't check if that Get failed, as if it did, it left info with default values
-
-        // if there is a bracket and it's not [0], then we're not an array, we're just an entry in an array
-        if (hadBracketPart && !bracketPart.EqualsLiteral("[0]")) {
-            info.isArray = false;
-            info.arraySize = 1;
-        }
-        return info;
-    }
-
-    WebGLContext *GetParentObject() const {
-        return Context();
-    }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
-
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLProgram)
-
-protected:
-
-    WebGLuint mGLName;
-    bool mLinkStatus;
-    // attached shaders of the program object
-    nsTArray<WebGLRefPtr<WebGLShader> > mAttachedShaders;
-    CheckedUint32 mGeneration;
-
-    // post-link data
-    std::vector<bool> mAttribsInUse;
-    nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap;
-    nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap;
-    int mAttribMaxNameLength;
-};
-
-
-class WebGLFramebufferAttachment
-{
-    // deleting a texture or renderbuffer immediately detaches it
-    WebGLRefPtr<WebGLTexture> mTexturePtr;
-    WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
-    WebGLenum mAttachmentPoint;
-    WebGLint mTextureLevel;
-    WebGLenum mTextureCubeMapFace;
-
-    friend class WebGLFramebuffer;
-
-public:
-    WebGLFramebufferAttachment(WebGLenum aAttachmentPoint)
-        : mAttachmentPoint(aAttachmentPoint)
-    {}
-
-    bool IsDefined() const {
-        return Texture() || Renderbuffer();
-    }
-
-    bool IsDeleteRequested() const {
-        return Texture() ? Texture()->IsDeleteRequested()
-             : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
-             : false;
-    }
-
-    bool HasAlpha() const {
-        WebGLenum format = 0;
-        if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace))
-            format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format();
-        else if (Renderbuffer())
-            format = Renderbuffer()->InternalFormat();
-        return format == LOCAL_GL_RGBA ||
-               format == LOCAL_GL_LUMINANCE_ALPHA ||
-               format == LOCAL_GL_ALPHA ||
-               format == LOCAL_GL_RGBA4 ||
-               format == LOCAL_GL_RGB5_A1;
-    }
-
-    void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) {
-        mTexturePtr = tex;
-        mRenderbufferPtr = nullptr;
-        mTextureLevel = level;
-        mTextureCubeMapFace = face;
-    }
-    void SetRenderbuffer(WebGLRenderbuffer *rb) {
-        mTexturePtr = nullptr;
-        mRenderbufferPtr = rb;
-    }
-    const WebGLTexture *Texture() const {
-        return mTexturePtr;
-    }
-    WebGLTexture *Texture() {
-        return mTexturePtr;
-    }
-    const WebGLRenderbuffer *Renderbuffer() const {
-        return mRenderbufferPtr;
-    }
-    WebGLRenderbuffer *Renderbuffer() {
-        return mRenderbufferPtr;
-    }
-    WebGLint TextureLevel() const {
-        return mTextureLevel;
-    }
-    WebGLenum TextureCubeMapFace() const {
-        return mTextureCubeMapFace;
-    }
-
-    bool HasUninitializedRenderbuffer() const {
-        return mRenderbufferPtr && !mRenderbufferPtr->Initialized();
-    }
-
-    void Reset() {
-        mTexturePtr = nullptr;
-        mRenderbufferPtr = nullptr;
-    }
-
-    const WebGLRectangleObject* RectangleObject() const {
-        if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace))
-            return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace);
-        else if (Renderbuffer())
-            return Renderbuffer();
-        else
-            return nullptr;
-    }
-    bool HasSameDimensionsAs(const WebGLFramebufferAttachment& other) const {
-        const WebGLRectangleObject *thisRect = RectangleObject();
-        const WebGLRectangleObject *otherRect = other.RectangleObject();
-        return thisRect &&
-               otherRect &&
-               thisRect->HasSameDimensionsAs(*otherRect);
-    }
-
-    bool IsComplete() const {
-        const WebGLRectangleObject *thisRect = RectangleObject();
-
-        if (!thisRect ||
-            !thisRect->Width() ||
-            !thisRect->Height())
-            return false;
-
-        if (mTexturePtr) {
-            if (!mTexturePtr->HasImageInfoAt(0, 0))
-                return false;
-
-            WebGLenum format = mTexturePtr->ImageInfoAt(0).Format();
-            switch (mAttachmentPoint)
-            {
-                case LOCAL_GL_COLOR_ATTACHMENT0:
-                    return format == LOCAL_GL_ALPHA ||
-                           format == LOCAL_GL_LUMINANCE ||
-                           format == LOCAL_GL_LUMINANCE_ALPHA ||
-                           format == LOCAL_GL_RGB ||
-                           format == LOCAL_GL_RGBA;
-                case LOCAL_GL_DEPTH_ATTACHMENT:
-                    return format == LOCAL_GL_DEPTH_COMPONENT;
-                case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-                    return format == LOCAL_GL_DEPTH_STENCIL;
-
-                default:
-                    MOZ_NOT_REACHED("Invalid WebGL texture format?");
-            }
-        } 
-
-        if (mRenderbufferPtr) {
-            WebGLenum format = mRenderbufferPtr->InternalFormat();
-            switch (mAttachmentPoint) {
-                case LOCAL_GL_COLOR_ATTACHMENT0:
-                    return format == LOCAL_GL_RGB565 ||
-                           format == LOCAL_GL_RGB5_A1 ||
-                           format == LOCAL_GL_RGBA4;
-                case LOCAL_GL_DEPTH_ATTACHMENT:
-                    return format == LOCAL_GL_DEPTH_COMPONENT16;
-                case LOCAL_GL_STENCIL_ATTACHMENT:
-                    return format == LOCAL_GL_STENCIL_INDEX8;
-                case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-                    return format == LOCAL_GL_DEPTH_STENCIL;
-                default:
-                    NS_ABORT(); // should have been validated earlier
-            }
-        }
-
-        NS_ABORT(); // should never get there
-        return false;
-    }
-};
-
-class WebGLFramebuffer MOZ_FINAL
-    : public nsISupports
-    , public WebGLRefCountedObject<WebGLFramebuffer>
-    , public LinkedListElement<WebGLFramebuffer>
-    , public WebGLContextBoundObject
-    , public nsWrapperCache
-{
-public:
-    WebGLFramebuffer(WebGLContext *context)
-        : WebGLContextBoundObject(context)
-        , mHasEverBeenBound(false)
-        , mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0)
-        , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
-        , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
-        , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-    {
-        SetIsDOMBinding();
-        mContext->MakeContextCurrent();
-        mContext->gl->fGenFramebuffers(1, &mGLName);
-        mContext->mFramebuffers.insertBack(this);
-    }
-
-    ~WebGLFramebuffer() {
-        DeleteOnce();
-    }
-
-    void Delete() {
-        mColorAttachment.Reset();
-        mDepthAttachment.Reset();
-        mStencilAttachment.Reset();
-        mDepthStencilAttachment.Reset();
-        mContext->MakeContextCurrent();
-        mContext->gl->fDeleteFramebuffers(1, &mGLName);
-        LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
-    }
-
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    WebGLuint GLName() { return mGLName; }
-
-    void FramebufferRenderbuffer(WebGLenum target,
-                                 WebGLenum attachment,
-                                 WebGLenum rbtarget,
-                                 WebGLRenderbuffer *wrb)
-    {
-        if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb))
-        {
-            return;
-        }
-
-        if (target != LOCAL_GL_FRAMEBUFFER)
-            return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
-
-        if (rbtarget != LOCAL_GL_RENDERBUFFER)
-            return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
-
-        switch (attachment) {
-        case LOCAL_GL_DEPTH_ATTACHMENT:
-            mDepthAttachment.SetRenderbuffer(wrb);
-            break;
-        case LOCAL_GL_STENCIL_ATTACHMENT:
-            mStencilAttachment.SetRenderbuffer(wrb);
-            break;
-        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-            mDepthStencilAttachment.SetRenderbuffer(wrb);
-            break;
-        default:
-            // finish checking that the 'attachment' parameter is among the allowed values
-            if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
-                return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment);
-
-            mColorAttachment.SetRenderbuffer(wrb);
-            break;
-        }
-
-        mContext->MakeContextCurrent();
-        WebGLuint parambuffername = wrb ? wrb->GLName() : 0;
-        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-            WebGLuint depthbuffername = parambuffername;
-            WebGLuint stencilbuffername = parambuffername;
-            if (!parambuffername){
-                depthbuffername   = mDepthAttachment.Renderbuffer()   ? mDepthAttachment.Renderbuffer()->GLName()   : 0;
-                stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0;
-            }
-            mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername);
-            mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername);
-        } else {
-            WebGLuint renderbuffername = parambuffername;
-            if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
-                renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0;
-            }
-            mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
-        }
-    }
-
-    void FramebufferTexture2D(WebGLenum target,
-                              WebGLenum attachment,
-                              WebGLenum textarget,
-                              WebGLTexture *wtex,
-                              WebGLint level)
-    {
-        if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
-                                               wtex))
-        {
-            return;
-        }
-
-        if (target != LOCAL_GL_FRAMEBUFFER)
-            return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
-
-        if (textarget != LOCAL_GL_TEXTURE_2D &&
-            (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
-             textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
-            return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
-
-        if (level != 0)
-            return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0");
-
-        size_t face = WebGLTexture::FaceForTarget(textarget);
-        switch (attachment) {
-        case LOCAL_GL_DEPTH_ATTACHMENT:
-            mDepthAttachment.SetTexture(wtex, level, face);
-            break;
-        case LOCAL_GL_STENCIL_ATTACHMENT:
-            mStencilAttachment.SetTexture(wtex, level, face);
-            break;
-        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-            mDepthStencilAttachment.SetTexture(wtex, level, face);
-            break;
-        default:
-            if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
-                return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
-
-            mColorAttachment.SetTexture(wtex, level, face);
-            break;
-        }
-
-        mContext->MakeContextCurrent();
-        WebGLuint paramtexturename = wtex ? wtex->GLName() : 0;
-        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-            WebGLuint depthtexturename = paramtexturename;
-            WebGLuint stenciltexturename = paramtexturename;
-            if(!paramtexturename){
-                depthtexturename   = mDepthAttachment.Texture()   ? mDepthAttachment.Texture()->GLName()   : 0;
-                stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0;
-            }
-            mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level);
-            mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level);
-        } else {
-            WebGLuint texturename = paramtexturename;
-            if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
-                texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0;
-            }
-            mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
-        }
-
-        return;
-    }
-
-    bool HasIncompleteAttachment() const {
-        return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) ||
-               (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) ||
-               (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) ||
-               (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete());
-    }
-
-    bool HasDepthStencilConflict() const {
-        return int(mDepthAttachment.IsDefined()) +
-               int(mStencilAttachment.IsDefined()) +
-               int(mDepthStencilAttachment.IsDefined()) >= 2;
-    }
-
-    bool HasAttachmentsOfMismatchedDimensions() const {
-        return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) ||
-               (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) ||
-               (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment));
-    }
-
-    const WebGLFramebufferAttachment& ColorAttachment() const {
-        return mColorAttachment;
-    }
-
-    const WebGLFramebufferAttachment& DepthAttachment() const {
-        return mDepthAttachment;
-    }
-
-    const WebGLFramebufferAttachment& StencilAttachment() const {
-        return mStencilAttachment;
-    }
-
-    const WebGLFramebufferAttachment& DepthStencilAttachment() const {
-        return mDepthStencilAttachment;
-    }
-
-    const WebGLFramebufferAttachment& GetAttachment(WebGLenum attachment) const {
-        if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-            return mDepthStencilAttachment;
-        if (attachment == LOCAL_GL_DEPTH_ATTACHMENT)
-            return mDepthAttachment;
-        if (attachment == LOCAL_GL_STENCIL_ATTACHMENT)
-            return mStencilAttachment;
-
-        NS_ASSERTION(attachment == LOCAL_GL_COLOR_ATTACHMENT0, "bad attachment!");
-        return mColorAttachment;
-    }
-
-    void DetachTexture(const WebGLTexture *tex) {
-        if (mColorAttachment.Texture() == tex)
-            FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0);
-        if (mDepthAttachment.Texture() == tex)
-            FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
-        if (mStencilAttachment.Texture() == tex)
-            FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
-        if (mDepthStencilAttachment.Texture() == tex)
-            FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
-    }
-
-    void DetachRenderbuffer(const WebGLRenderbuffer *rb) {
-        if (mColorAttachment.Renderbuffer() == rb)
-            FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr);
-        if (mDepthAttachment.Renderbuffer() == rb)
-            FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
-        if (mStencilAttachment.Renderbuffer() == rb)
-            FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
-        if (mDepthStencilAttachment.Renderbuffer() == rb)
-            FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
-    }
-
-    const WebGLRectangleObject *RectangleObject() {
-        return mColorAttachment.RectangleObject();
-    }
-
-    WebGLContext *GetParentObject() const {
-        return Context();
-    }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
-
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLFramebuffer)
-
-    bool CheckAndInitializeRenderbuffers()
-    {
-        // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not
-        // generate the INVALID_FRAMEBUFFER_OPERATION that we need here
-        if (HasDepthStencilConflict())
-            return false;
-        
-        if (HasIncompleteAttachment())
-            return false;
-
-        if (!mColorAttachment.HasUninitializedRenderbuffer() &&
-            !mDepthAttachment.HasUninitializedRenderbuffer() &&
-            !mStencilAttachment.HasUninitializedRenderbuffer() &&
-            !mDepthStencilAttachment.HasUninitializedRenderbuffer())
-            return true;
-
-        // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case
-        const WebGLRectangleObject *rect = mColorAttachment.RectangleObject();
-        if (!rect ||
-            !rect->Width() ||
-            !rect->Height())
-            return false;
-
-        mContext->MakeContextCurrent();
-
-        WebGLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
-            return false;
-
-        uint32_t mask = 0;
-
-        if (mColorAttachment.HasUninitializedRenderbuffer())
-            mask |= LOCAL_GL_COLOR_BUFFER_BIT;
-
-        if (mDepthAttachment.HasUninitializedRenderbuffer() ||
-            mDepthStencilAttachment.HasUninitializedRenderbuffer())
-        {
-            mask |= LOCAL_GL_DEPTH_BUFFER_BIT;
-        }
-
-        if (mStencilAttachment.HasUninitializedRenderbuffer() ||
-            mDepthStencilAttachment.HasUninitializedRenderbuffer())
-        {
-            mask |= LOCAL_GL_STENCIL_BUFFER_BIT;
-        }
-
-        mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height()));
-
-        if (mColorAttachment.HasUninitializedRenderbuffer())
-            mColorAttachment.Renderbuffer()->SetInitialized(true);
-
-        if (mDepthAttachment.HasUninitializedRenderbuffer())
-            mDepthAttachment.Renderbuffer()->SetInitialized(true);
-
-        if (mStencilAttachment.HasUninitializedRenderbuffer())
-            mStencilAttachment.Renderbuffer()->SetInitialized(true);
-
-        if (mDepthStencilAttachment.HasUninitializedRenderbuffer())
-            mDepthStencilAttachment.Renderbuffer()->SetInitialized(true);
-
-        return true;
-    }
-
-    WebGLuint mGLName;
-    bool mHasEverBeenBound;
-
-    // we only store pointers to attached renderbuffers, not to attached textures, because
-    // we will only need to initialize renderbuffers. Textures are already initialized.
-    WebGLFramebufferAttachment mColorAttachment,
-                               mDepthAttachment,
-                               mStencilAttachment,
-                               mDepthStencilAttachment;
-};
-
-class WebGLUniformLocation MOZ_FINAL
-    : public nsISupports
-    , public WebGLContextBoundObject
-{
-public:
-    WebGLUniformLocation(WebGLContext *context, WebGLProgram *program, GLint location, const WebGLUniformInfo& info)
-        : WebGLContextBoundObject(context)
-        , mProgram(program)
-        , mProgramGeneration(program->Generation())
-        , mLocation(location)
-        , mInfo(info)
-    {
-        mElementSize = info.ElementSize();
-    }
-
-    ~WebGLUniformLocation() {
-    }
-
-    // needed for certain helper functions like ValidateObject.
-    // WebGLUniformLocation's can't be 'Deleted' in the WebGL sense.
-    bool IsDeleted() const { return false; }
-
-    const WebGLUniformInfo &Info() const { return mInfo; }
-
-    WebGLProgram *Program() const { return mProgram; }
-    GLint Location() const { return mLocation; }
-    uint32_t ProgramGeneration() const { return mProgramGeneration; }
-    int ElementSize() const { return mElementSize; }
-
-    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope);
-
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_CLASS(WebGLUniformLocation)
-
-protected:
-    // nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted.
-    // we just want to avoid having a dangling pointer.
-    nsRefPtr<WebGLProgram> mProgram;
-
-    uint32_t mProgramGeneration;
-    GLint mLocation;
-    WebGLUniformInfo mInfo;
-    int mElementSize;
-    friend class WebGLProgram;
-};
-
 class WebGLActiveInfo MOZ_FINAL
     : public nsISupports
 {
 public:
     WebGLActiveInfo(WebGLint size, WebGLenum type, const nsACString& name) :
         mSize(size),
         mType(type),
         mName(NS_ConvertASCIItoUTF16(name))
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -2246,17 +2246,17 @@ WebGLContext::GetFramebufferAttachmentPa
 
     if (!mBoundFramebuffer) {
         ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query framebuffer 0");
         return JS::NullValue();
     }
 
     MakeContextCurrent();
 
-    const WebGLFramebufferAttachment& fba = mBoundFramebuffer->GetAttachment(attachment);
+    const WebGLFramebuffer::Attachment& fba = mBoundFramebuffer->GetAttachment(attachment);
 
     if (fba.Renderbuffer()) {
         switch (pname) {
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                 return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER));
 
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
             {
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -33,18 +33,23 @@ WebGLProgram::UpdateInfo()
     mAttribMaxNameLength = 0;
 
     for (size_t i = 0; i < mAttachedShaders.Length(); i++)
         mAttribMaxNameLength = NS_MAX(mAttribMaxNameLength, mAttachedShaders[i]->mAttribMaxNameLength);
 
     GLint attribCount;
     mContext->gl->fGetProgramiv(mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES, &attribCount);
 
-    mAttribsInUse.resize(mContext->mGLMaxVertexAttribs);
-    std::fill(mAttribsInUse.begin(), mAttribsInUse.end(), false);
+    if (!mAttribsInUse.SetLength(mContext->mGLMaxVertexAttribs)) {
+        mContext->ErrorOutOfMemory("updateInfo: out of memory to allocate %d attribs", mContext->mGLMaxVertexAttribs);
+        return false;
+    }
+
+    for (size_t i = 0; i < mAttribsInUse.Length(); i++)
+        mAttribsInUse[i] = false;
 
     nsAutoArrayPtr<char> nameBuf(new char[mAttribMaxNameLength]);
 
     for (int i = 0; i < attribCount; ++i) {
         GLint attrnamelen;
         GLint attrsize;
         GLenum attrtype;
         mContext->gl->fGetActiveAttrib(mGLName, i, mAttribMaxNameLength, &attrnamelen, &attrsize, &attrtype, nameBuf);
@@ -847,16 +852,17 @@ WebGLContext::InitAndValidateGL()
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during OpenGL context initialization, before WebGL initialization!", error);
         return false;
     }
 
     mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
     mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
+    mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false);
 
     mActiveTexture = 0;
     mWebGLError = LOCAL_GL_NO_ERROR;
 
     mAttribBuffers.Clear();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
--- a/content/canvas/src/WebGLFramebuffer.cpp
+++ b/content/canvas/src/WebGLFramebuffer.cpp
@@ -1,23 +1,378 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
+#include "WebGLFramebuffer.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 using namespace mozilla;
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) {
     return dom::WebGLFramebufferBinding::Wrap(cx, scope, this, triedToWrap);
 }
 
+WebGLFramebuffer::WebGLFramebuffer(WebGLContext *context)
+    : WebGLContextBoundObject(context)
+    , mHasEverBeenBound(false)
+    , mColorAttachment(LOCAL_GL_COLOR_ATTACHMENT0)
+    , mDepthAttachment(LOCAL_GL_DEPTH_ATTACHMENT)
+    , mStencilAttachment(LOCAL_GL_STENCIL_ATTACHMENT)
+    , mDepthStencilAttachment(LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+{
+    SetIsDOMBinding();
+    mContext->MakeContextCurrent();
+    mContext->gl->fGenFramebuffers(1, &mGLName);
+    mContext->mFramebuffers.insertBack(this);
+}
+
+bool
+WebGLFramebuffer::Attachment::IsDeleteRequested() const {
+    return Texture() ? Texture()->IsDeleteRequested()
+         : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
+         : false;
+}
+
+bool
+WebGLFramebuffer::Attachment::HasAlpha() const {
+    WebGLenum format = 0;
+    if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace))
+        format = Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace).Format();
+    else if (Renderbuffer())
+        format = Renderbuffer()->InternalFormat();
+    return format == LOCAL_GL_RGBA ||
+           format == LOCAL_GL_LUMINANCE_ALPHA ||
+           format == LOCAL_GL_ALPHA ||
+           format == LOCAL_GL_RGBA4 ||
+           format == LOCAL_GL_RGB5_A1;
+}
+
+void
+WebGLFramebuffer::Attachment::SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face) {
+    mTexturePtr = tex;
+    mRenderbufferPtr = nullptr;
+    mTextureLevel = level;
+    mTextureCubeMapFace = face;
+}
+
+bool
+WebGLFramebuffer::Attachment::HasUninitializedRenderbuffer() const {
+    return mRenderbufferPtr && !mRenderbufferPtr->Initialized();
+}
+
+const WebGLRectangleObject*
+WebGLFramebuffer::Attachment::RectangleObject() const {
+    if (Texture() && Texture()->HasImageInfoAt(mTextureLevel, mTextureCubeMapFace))
+        return &Texture()->ImageInfoAt(mTextureLevel, mTextureCubeMapFace);
+    else if (Renderbuffer())
+        return Renderbuffer();
+    else
+        return nullptr;
+}
+
+bool
+WebGLFramebuffer::Attachment::HasSameDimensionsAs(const Attachment& other) const {
+    const WebGLRectangleObject *thisRect = RectangleObject();
+    const WebGLRectangleObject *otherRect = other.RectangleObject();
+    return thisRect &&
+           otherRect &&
+           thisRect->HasSameDimensionsAs(*otherRect);
+}
+
+bool
+WebGLFramebuffer::Attachment::IsComplete() const {
+    const WebGLRectangleObject *thisRect = RectangleObject();
+
+    if (!thisRect ||
+        !thisRect->Width() ||
+        !thisRect->Height())
+        return false;
+
+    if (mTexturePtr) {
+        if (!mTexturePtr->HasImageInfoAt(0, 0))
+            return false;
+
+        WebGLenum format = mTexturePtr->ImageInfoAt(0).Format();
+        switch (mAttachmentPoint)
+        {
+            case LOCAL_GL_COLOR_ATTACHMENT0:
+                return format == LOCAL_GL_ALPHA ||
+                       format == LOCAL_GL_LUMINANCE ||
+                       format == LOCAL_GL_LUMINANCE_ALPHA ||
+                       format == LOCAL_GL_RGB ||
+                       format == LOCAL_GL_RGBA;
+            case LOCAL_GL_DEPTH_ATTACHMENT:
+                return format == LOCAL_GL_DEPTH_COMPONENT;
+            case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+                return format == LOCAL_GL_DEPTH_STENCIL;
+
+            default:
+                MOZ_NOT_REACHED("Invalid WebGL texture format?");
+        }
+    }
+
+    if (mRenderbufferPtr) {
+        WebGLenum format = mRenderbufferPtr->InternalFormat();
+        switch (mAttachmentPoint) {
+            case LOCAL_GL_COLOR_ATTACHMENT0:
+                return format == LOCAL_GL_RGB565 ||
+                       format == LOCAL_GL_RGB5_A1 ||
+                       format == LOCAL_GL_RGBA4;
+            case LOCAL_GL_DEPTH_ATTACHMENT:
+                return format == LOCAL_GL_DEPTH_COMPONENT16;
+            case LOCAL_GL_STENCIL_ATTACHMENT:
+                return format == LOCAL_GL_STENCIL_INDEX8;
+            case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+                return format == LOCAL_GL_DEPTH_STENCIL;
+            default:
+                NS_ABORT(); // should have been validated earlier
+        }
+    }
+
+    NS_ABORT(); // should never get there
+    return false;
+}
+
+void
+WebGLFramebuffer::Delete() {
+    mColorAttachment.Reset();
+    mDepthAttachment.Reset();
+    mStencilAttachment.Reset();
+    mDepthStencilAttachment.Reset();
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteFramebuffers(1, &mGLName);
+    LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
+}
+
+void
+WebGLFramebuffer::FramebufferRenderbuffer(WebGLenum target,
+                             WebGLenum attachment,
+                             WebGLenum rbtarget,
+                             WebGLRenderbuffer *wrb)
+{
+    if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", wrb))
+    {
+        return;
+    }
+
+    if (target != LOCAL_GL_FRAMEBUFFER)
+        return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
+
+    if (rbtarget != LOCAL_GL_RENDERBUFFER)
+        return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
+
+    switch (attachment) {
+    case LOCAL_GL_DEPTH_ATTACHMENT:
+        mDepthAttachment.SetRenderbuffer(wrb);
+        break;
+    case LOCAL_GL_STENCIL_ATTACHMENT:
+        mStencilAttachment.SetRenderbuffer(wrb);
+        break;
+    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+        mDepthStencilAttachment.SetRenderbuffer(wrb);
+        break;
+    default:
+        // finish checking that the 'attachment' parameter is among the allowed values
+        if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
+            return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment);
+
+        mColorAttachment.SetRenderbuffer(wrb);
+        break;
+    }
+
+    mContext->MakeContextCurrent();
+    WebGLuint parambuffername = wrb ? wrb->GLName() : 0;
+    if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+        WebGLuint depthbuffername = parambuffername;
+        WebGLuint stencilbuffername = parambuffername;
+        if (!parambuffername){
+            depthbuffername   = mDepthAttachment.Renderbuffer()   ? mDepthAttachment.Renderbuffer()->GLName()   : 0;
+            stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0;
+        }
+        mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername);
+        mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername);
+    } else {
+        WebGLuint renderbuffername = parambuffername;
+        if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
+            renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0;
+        }
+        mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
+    }
+}
+
+void
+WebGLFramebuffer::FramebufferTexture2D(WebGLenum target,
+                          WebGLenum attachment,
+                          WebGLenum textarget,
+                          WebGLTexture *wtex,
+                          WebGLint level)
+{
+    if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture",
+                                           wtex))
+    {
+        return;
+    }
+
+    if (target != LOCAL_GL_FRAMEBUFFER)
+        return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
+
+    if (textarget != LOCAL_GL_TEXTURE_2D &&
+        (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+         textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+        return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
+
+    if (level != 0)
+        return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0");
+
+    size_t face = WebGLTexture::FaceForTarget(textarget);
+    switch (attachment) {
+    case LOCAL_GL_DEPTH_ATTACHMENT:
+        mDepthAttachment.SetTexture(wtex, level, face);
+        break;
+    case LOCAL_GL_STENCIL_ATTACHMENT:
+        mStencilAttachment.SetTexture(wtex, level, face);
+        break;
+    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+        mDepthStencilAttachment.SetTexture(wtex, level, face);
+        break;
+    default:
+        if (attachment != LOCAL_GL_COLOR_ATTACHMENT0)
+            return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
+
+        mColorAttachment.SetTexture(wtex, level, face);
+        break;
+    }
+
+    mContext->MakeContextCurrent();
+    WebGLuint paramtexturename = wtex ? wtex->GLName() : 0;
+    if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
+        WebGLuint depthtexturename = paramtexturename;
+        WebGLuint stenciltexturename = paramtexturename;
+        if(!paramtexturename){
+            depthtexturename   = mDepthAttachment.Texture()   ? mDepthAttachment.Texture()->GLName()   : 0;
+            stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0;
+        }
+        mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level);
+        mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level);
+    } else {
+        WebGLuint texturename = paramtexturename;
+        if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
+            texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0;
+        }
+        mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
+    }
+
+    return;
+}
+
+const WebGLFramebuffer::Attachment&
+WebGLFramebuffer::GetAttachment(WebGLenum attachment) const {
+    if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+        return mDepthStencilAttachment;
+    if (attachment == LOCAL_GL_DEPTH_ATTACHMENT)
+        return mDepthAttachment;
+    if (attachment == LOCAL_GL_STENCIL_ATTACHMENT)
+        return mStencilAttachment;
+
+    NS_ASSERTION(attachment == LOCAL_GL_COLOR_ATTACHMENT0, "bad attachment!");
+    return mColorAttachment;
+}
+
+void
+WebGLFramebuffer::DetachTexture(const WebGLTexture *tex) {
+    if (mColorAttachment.Texture() == tex)
+        FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, nullptr, 0);
+    if (mDepthAttachment.Texture() == tex)
+        FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
+    if (mStencilAttachment.Texture() == tex)
+        FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
+    if (mDepthStencilAttachment.Texture() == tex)
+        FramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_TEXTURE_2D, nullptr, 0);
+}
+
+void
+WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer *rb) {
+    if (mColorAttachment.Renderbuffer() == rb)
+        FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, nullptr);
+    if (mDepthAttachment.Renderbuffer() == rb)
+        FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
+    if (mStencilAttachment.Renderbuffer() == rb)
+        FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
+    if (mDepthStencilAttachment.Renderbuffer() == rb)
+        FramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT, LOCAL_GL_RENDERBUFFER, nullptr);
+}
+
+bool
+WebGLFramebuffer::CheckAndInitializeRenderbuffers()
+{
+    // enforce WebGL section 6.5 which is WebGL-specific, hence OpenGL itself would not
+    // generate the INVALID_FRAMEBUFFER_OPERATION that we need here
+    if (HasDepthStencilConflict())
+        return false;
+
+    if (HasIncompleteAttachment())
+        return false;
+
+    if (!mColorAttachment.HasUninitializedRenderbuffer() &&
+        !mDepthAttachment.HasUninitializedRenderbuffer() &&
+        !mStencilAttachment.HasUninitializedRenderbuffer() &&
+        !mDepthStencilAttachment.HasUninitializedRenderbuffer())
+        return true;
+
+    // ensure INVALID_FRAMEBUFFER_OPERATION in zero-size case
+    const WebGLRectangleObject *rect = mColorAttachment.RectangleObject();
+    if (!rect ||
+        !rect->Width() ||
+        !rect->Height())
+        return false;
+
+    mContext->MakeContextCurrent();
+
+    WebGLenum status = mContext->CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+        return false;
+
+    uint32_t mask = 0;
+
+    if (mColorAttachment.HasUninitializedRenderbuffer())
+        mask |= LOCAL_GL_COLOR_BUFFER_BIT;
+
+    if (mDepthAttachment.HasUninitializedRenderbuffer() ||
+        mDepthStencilAttachment.HasUninitializedRenderbuffer())
+    {
+        mask |= LOCAL_GL_DEPTH_BUFFER_BIT;
+    }
+
+    if (mStencilAttachment.HasUninitializedRenderbuffer() ||
+        mDepthStencilAttachment.HasUninitializedRenderbuffer())
+    {
+        mask |= LOCAL_GL_STENCIL_BUFFER_BIT;
+    }
+
+    mContext->ForceClearFramebufferWithDefaultValues(mask, nsIntRect(0, 0, rect->Width(), rect->Height()));
+
+    if (mColorAttachment.HasUninitializedRenderbuffer())
+        mColorAttachment.Renderbuffer()->SetInitialized(true);
+
+    if (mDepthAttachment.HasUninitializedRenderbuffer())
+        mDepthAttachment.Renderbuffer()->SetInitialized(true);
+
+    if (mStencilAttachment.HasUninitializedRenderbuffer())
+        mStencilAttachment.Renderbuffer()->SetInitialized(true);
+
+    if (mDepthStencilAttachment.HasUninitializedRenderbuffer())
+        mDepthStencilAttachment.Renderbuffer()->SetInitialized(true);
+
+    return true;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_8(WebGLFramebuffer,
   mColorAttachment.mTexturePtr,
   mColorAttachment.mRenderbufferPtr,
   mDepthAttachment.mTexturePtr,
   mDepthAttachment.mRenderbufferPtr,
   mStencilAttachment.mTexturePtr,
   mStencilAttachment.mRenderbufferPtr,
   mDepthStencilAttachment.mTexturePtr,
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLFramebuffer.h
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLFRAMEBUFFER_H_
+#define WEBGLFRAMEBUFFER_H_
+
+#include "WebGLObjectModel.h"
+
+#include "nsWrapperCache.h"
+
+#include "mozilla/LinkedList.h"
+
+namespace mozilla {
+
+class WebGLTexture;
+class WebGLRenderbuffer;
+
+class WebGLFramebuffer MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLFramebuffer>
+    , public LinkedListElement<WebGLFramebuffer>
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+public:
+    WebGLFramebuffer(WebGLContext *context);
+
+    ~WebGLFramebuffer() {
+        DeleteOnce();
+    }
+
+    class Attachment
+    {
+        // deleting a texture or renderbuffer immediately detaches it
+        WebGLRefPtr<WebGLTexture> mTexturePtr;
+        WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
+        WebGLenum mAttachmentPoint;
+        WebGLint mTextureLevel;
+        WebGLenum mTextureCubeMapFace;
+
+        friend class WebGLFramebuffer;
+
+    public:
+        Attachment(WebGLenum aAttachmentPoint)
+            : mAttachmentPoint(aAttachmentPoint)
+        {}
+
+        bool IsDefined() const {
+            return Texture() || Renderbuffer();
+        }
+
+        bool IsDeleteRequested() const;
+
+        bool HasAlpha() const;
+
+        void SetTexture(WebGLTexture *tex, WebGLint level, WebGLenum face);
+        void SetRenderbuffer(WebGLRenderbuffer *rb) {
+            mTexturePtr = nullptr;
+            mRenderbufferPtr = rb;
+        }
+        const WebGLTexture *Texture() const {
+            return mTexturePtr;
+        }
+        WebGLTexture *Texture() {
+            return mTexturePtr;
+        }
+        const WebGLRenderbuffer *Renderbuffer() const {
+            return mRenderbufferPtr;
+        }
+        WebGLRenderbuffer *Renderbuffer() {
+            return mRenderbufferPtr;
+        }
+        WebGLint TextureLevel() const {
+            return mTextureLevel;
+        }
+        WebGLenum TextureCubeMapFace() const {
+            return mTextureCubeMapFace;
+        }
+
+        bool HasUninitializedRenderbuffer() const;
+
+        void Reset() {
+            mTexturePtr = nullptr;
+            mRenderbufferPtr = nullptr;
+        }
+
+        const WebGLRectangleObject* RectangleObject() const;
+        bool HasSameDimensionsAs(const Attachment& other) const;
+
+        bool IsComplete() const;
+    };
+
+    void Delete();
+
+    bool HasEverBeenBound() { return mHasEverBeenBound; }
+    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
+    WebGLuint GLName() { return mGLName; }
+
+    void FramebufferRenderbuffer(WebGLenum target,
+                                 WebGLenum attachment,
+                                 WebGLenum rbtarget,
+                                 WebGLRenderbuffer *wrb);
+
+    void FramebufferTexture2D(WebGLenum target,
+                              WebGLenum attachment,
+                              WebGLenum textarget,
+                              WebGLTexture *wtex,
+                              WebGLint level);
+
+    bool HasIncompleteAttachment() const {
+        return (mColorAttachment.IsDefined() && !mColorAttachment.IsComplete()) ||
+               (mDepthAttachment.IsDefined() && !mDepthAttachment.IsComplete()) ||
+               (mStencilAttachment.IsDefined() && !mStencilAttachment.IsComplete()) ||
+               (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.IsComplete());
+    }
+
+    bool HasDepthStencilConflict() const {
+        return int(mDepthAttachment.IsDefined()) +
+               int(mStencilAttachment.IsDefined()) +
+               int(mDepthStencilAttachment.IsDefined()) >= 2;
+    }
+
+    bool HasAttachmentsOfMismatchedDimensions() const {
+        return (mDepthAttachment.IsDefined() && !mDepthAttachment.HasSameDimensionsAs(mColorAttachment)) ||
+               (mStencilAttachment.IsDefined() && !mStencilAttachment.HasSameDimensionsAs(mColorAttachment)) ||
+               (mDepthStencilAttachment.IsDefined() && !mDepthStencilAttachment.HasSameDimensionsAs(mColorAttachment));
+    }
+
+    const Attachment& ColorAttachment() const {
+        return mColorAttachment;
+    }
+
+    const Attachment& DepthAttachment() const {
+        return mDepthAttachment;
+    }
+
+    const Attachment& StencilAttachment() const {
+        return mStencilAttachment;
+    }
+
+    const Attachment& DepthStencilAttachment() const {
+        return mDepthStencilAttachment;
+    }
+
+    const Attachment& GetAttachment(WebGLenum attachment) const;
+
+    void DetachTexture(const WebGLTexture *tex);
+
+    void DetachRenderbuffer(const WebGLRenderbuffer *rb);
+
+    const WebGLRectangleObject *RectangleObject() {
+        return mColorAttachment.RectangleObject();
+    }
+
+    WebGLContext *GetParentObject() const {
+        return Context();
+    }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLFramebuffer)
+
+    bool CheckAndInitializeRenderbuffers();
+
+    WebGLuint mGLName;
+    bool mHasEverBeenBound;
+
+    // we only store pointers to attached renderbuffers, not to attached textures, because
+    // we will only need to initialize renderbuffers. Textures are already initialized.
+    Attachment mColorAttachment,
+               mDepthAttachment,
+               mStencilAttachment,
+               mDepthStencilAttachment;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/canvas/src/WebGLProgram.cpp
+++ b/content/canvas/src/WebGLProgram.cpp
@@ -1,23 +1,265 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
+#include "WebGLProgram.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 using namespace mozilla;
 
+/** Takes an ASCII string like "foo[i]", turns it into "foo" and returns "[i]" in bracketPart
+  * 
+  * \param string input/output: the string to split, becomes the string without the bracket part
+  * \param bracketPart output: gets the bracket part.
+  * 
+  * Notice that if there are multiple brackets like "foo[i].bar[j]", only the last bracket is split.
+  */
+static bool SplitLastSquareBracket(nsACString& string, nsCString& bracketPart)
+{
+    MOZ_ASSERT(bracketPart.IsEmpty(), "SplitLastSquareBracket must be called with empty bracketPart string");
+
+    if (string.IsEmpty())
+        return false;
+
+    char *string_start = string.BeginWriting();
+    char *s = string_start + string.Length() - 1;
+
+    if (*s != ']')
+        return false;
+
+    while (*s != '[' && s != string_start)
+        s--;
+
+    if (*s != '[')
+        return false;
+
+    bracketPart.Assign(s);
+    *s = 0;
+    string.EndWriting();
+    string.SetLength(s - string_start);
+    return true;
+}
+
 JSObject*
 WebGLProgram::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) {
     return dom::WebGLProgramBinding::Wrap(cx, scope, this, triedToWrap);
 }
 
+WebGLProgram::WebGLProgram(WebGLContext *context)
+    : WebGLContextBoundObject(context)
+    , mLinkStatus(false)
+    , mGeneration(0)
+    , mAttribMaxNameLength(0)
+{
+    SetIsDOMBinding();
+    mContext->MakeContextCurrent();
+    mGLName = mContext->gl->fCreateProgram();
+    mContext->mPrograms.insertBack(this);
+}
+
+void
+WebGLProgram::Delete() {
+    DetachShaders();
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteProgram(mGLName);
+    LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
+}
+
+bool
+WebGLProgram::AttachShader(WebGLShader *shader) {
+    if (ContainsShader(shader))
+        return false;
+    mAttachedShaders.AppendElement(shader);
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fAttachShader(GLName(), shader->GLName());
+
+    return true;
+}
+
+bool
+WebGLProgram::DetachShader(WebGLShader *shader) {
+    if (!mAttachedShaders.RemoveElement(shader))
+        return false;
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fDetachShader(GLName(), shader->GLName());
+
+    return true;
+}
+
+bool
+WebGLProgram::HasAttachedShaderOfType(GLenum shaderType) {
+    for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
+        if (mAttachedShaders[i] && mAttachedShaders[i]->ShaderType() == shaderType) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+WebGLProgram::HasBadShaderAttached() {
+    for (uint32_t i = 0; i < mAttachedShaders.Length(); ++i) {
+        if (mAttachedShaders[i] && !mAttachedShaders[i]->CompileStatus()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+size_t
+WebGLProgram::UpperBoundNumSamplerUniforms() {
+    size_t numSamplerUniforms = 0;
+    for (size_t i = 0; i < mAttachedShaders.Length(); ++i) {
+        const WebGLShader *shader = mAttachedShaders[i];
+        if (!shader)
+            continue;
+        for (size_t j = 0; j < shader->mUniformInfos.Length(); ++j) {
+            WebGLUniformInfo u = shader->mUniformInfos[j];
+            if (u.type == SH_SAMPLER_2D ||
+                u.type == SH_SAMPLER_CUBE)
+            {
+                numSamplerUniforms += u.arraySize;
+            }
+        }
+    }
+    return numSamplerUniforms;
+}
+
+void
+WebGLProgram::MapIdentifier(const nsACString& name, nsCString *mappedName) {
+    if (!mIdentifierMap) {
+        // if the identifier map doesn't exist yet, build it now
+        mIdentifierMap = new CStringMap;
+        mIdentifierMap->Init();
+        for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
+            for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
+                const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
+                mIdentifierMap->Put(attrib.original, attrib.mapped);
+            }
+            for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
+                const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
+                mIdentifierMap->Put(uniform.original, uniform.mapped);
+            }
+        }
+    }
+
+    nsCString mutableName(name);
+    nsCString bracketPart;
+    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
+    if (hadBracketPart)
+        mutableName.AppendLiteral("[0]");
+
+    if (mIdentifierMap->Get(mutableName, mappedName)) {
+        if (hadBracketPart) {
+            nsCString mappedBracketPart;
+            bool mappedHadBracketPart = SplitLastSquareBracket(*mappedName, mappedBracketPart);
+            if (mappedHadBracketPart)
+                mappedName->Append(bracketPart);
+        }
+        return;
+    }
+
+    // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform
+    // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0].
+    mutableName.AppendLiteral("[0]");
+    if (mIdentifierMap->Get(mutableName, mappedName))
+        return;
+
+    // not found? return name unchanged. This case happens e.g. on bad user input, or when
+    // we're not using identifier mapping, or if we didn't store an identifier in the map because
+    // e.g. its mapping is trivial (as happens for short identifiers)
+    mappedName->Assign(name);
+}
+
+void
+WebGLProgram::ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName) {
+    if (!mIdentifierReverseMap) {
+        // if the identifier reverse map doesn't exist yet, build it now
+        mIdentifierReverseMap = new CStringMap;
+        mIdentifierReverseMap->Init();
+        for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
+            for (size_t j = 0; j < mAttachedShaders[i]->mAttributes.Length(); j++) {
+                const WebGLMappedIdentifier& attrib = mAttachedShaders[i]->mAttributes[j];
+                mIdentifierReverseMap->Put(attrib.mapped, attrib.original);
+            }
+            for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
+                const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
+                mIdentifierReverseMap->Put(uniform.mapped, uniform.original);
+            }
+        }
+    }
+
+    nsCString mutableName(name);
+    nsCString bracketPart;
+    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
+    if (hadBracketPart)
+        mutableName.AppendLiteral("[0]");
+
+    if (mIdentifierReverseMap->Get(mutableName, reverseMappedName)) {
+        if (hadBracketPart) {
+            nsCString reverseMappedBracketPart;
+            bool reverseMappedHadBracketPart = SplitLastSquareBracket(*reverseMappedName, reverseMappedBracketPart);
+            if (reverseMappedHadBracketPart)
+                reverseMappedName->Append(bracketPart);
+        }
+        return;
+    }
+
+    // not found? We might be in the situation we have a uniform array name and the GL's glGetActiveUniform
+    // returned its name without [0], as is allowed by desktop GL but not in ES. Let's then try with [0].
+    mutableName.AppendLiteral("[0]");
+    if (mIdentifierReverseMap->Get(mutableName, reverseMappedName))
+        return;
+
+    // not found? return name unchanged. This case happens e.g. on bad user input, or when
+    // we're not using identifier mapping, or if we didn't store an identifier in the map because
+    // e.g. its mapping is trivial (as happens for short identifiers)
+    reverseMappedName->Assign(name);
+}
+
+WebGLUniformInfo
+WebGLProgram::GetUniformInfoForMappedIdentifier(const nsACString& name) {
+    if (!mUniformInfoMap) {
+        // if the identifier-to-array-size map doesn't exist yet, build it now
+        mUniformInfoMap = new CStringToUniformInfoMap;
+        mUniformInfoMap->Init();
+        for (size_t i = 0; i < mAttachedShaders.Length(); i++) {
+            for (size_t j = 0; j < mAttachedShaders[i]->mUniforms.Length(); j++) {
+                const WebGLMappedIdentifier& uniform = mAttachedShaders[i]->mUniforms[j];
+                const WebGLUniformInfo& info = mAttachedShaders[i]->mUniformInfos[j];
+                mUniformInfoMap->Put(uniform.mapped, info);
+            }
+        }
+    }
+
+    nsCString mutableName(name);
+    nsCString bracketPart;
+    bool hadBracketPart = SplitLastSquareBracket(mutableName, bracketPart);
+    // if there is a bracket, we're either an array or an entry in an array.
+    if (hadBracketPart)
+        mutableName.AppendLiteral("[0]");
+
+    WebGLUniformInfo info;
+    mUniformInfoMap->Get(mutableName, &info);
+    // we don't check if that Get failed, as if it did, it left info with default values
+
+    // if there is a bracket and it's not [0], then we're not an array, we're just an entry in an array
+    if (hadBracketPart && !bracketPart.EqualsLiteral("[0]")) {
+        info.isArray = false;
+        info.arraySize = 1;
+    }
+    return info;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WebGLProgram, mAttachedShaders)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLProgram)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLProgram)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLProgram)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLProgram.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLPROGRAM_H_
+#define WEBGLPROGRAM_H_
+
+#include "WebGLObjectModel.h"
+#include "WebGLShader.h"
+
+#include "nsWrapperCache.h"
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/CheckedInt.h"
+
+namespace mozilla {
+
+typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap;
+typedef nsDataHashtable<nsCStringHashKey, WebGLUniformInfo> CStringToUniformInfoMap;
+
+class WebGLProgram MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLProgram>
+    , public LinkedListElement<WebGLProgram>
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+public:
+    WebGLProgram(WebGLContext *context);
+
+    ~WebGLProgram() {
+        DeleteOnce();
+    }
+
+    void Delete();
+
+    void DetachShaders() {
+        mAttachedShaders.Clear();
+    }
+
+    WebGLuint GLName() { return mGLName; }
+    const nsTArray<WebGLRefPtr<WebGLShader> >& AttachedShaders() const { return mAttachedShaders; }
+    bool LinkStatus() { return mLinkStatus; }
+    uint32_t Generation() const { return mGeneration.value(); }
+    void SetLinkStatus(bool val) { mLinkStatus = val; }
+
+    bool ContainsShader(WebGLShader *shader) {
+        return mAttachedShaders.Contains(shader);
+    }
+
+    // return true if the shader wasn't already attached
+    bool AttachShader(WebGLShader *shader);
+
+    // return true if the shader was found and removed
+    bool DetachShader(WebGLShader *shader);
+
+    bool HasAttachedShaderOfType(GLenum shaderType);
+
+    bool HasBothShaderTypesAttached() {
+        return
+            HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) &&
+            HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER);
+    }
+
+    bool HasBadShaderAttached();
+
+    size_t UpperBoundNumSamplerUniforms();
+
+    bool NextGeneration()
+    {
+        if (!(mGeneration + 1).isValid())
+            return false; // must exit without changing mGeneration
+        ++mGeneration;
+        return true;
+    }
+
+    /* Called only after LinkProgram */
+    bool UpdateInfo();
+
+    /* Getters for cached program info */
+    bool IsAttribInUse(unsigned i) const { return mAttribsInUse[i]; }
+
+    /* Maps identifier |name| to the mapped identifier |*mappedName|
+     * Both are ASCII strings.
+     */
+    void MapIdentifier(const nsACString& name, nsCString *mappedName);
+
+    /* Un-maps mapped identifier |name| to the original identifier |*reverseMappedName|
+     * Both are ASCII strings.
+     */
+    void ReverseMapIdentifier(const nsACString& name, nsCString *reverseMappedName);
+
+    /* Returns the uniform array size (or 1 if the uniform is not an array) of
+     * the uniform with given mapped identifier.
+     *
+     * Note: the input string |name| is the mapped identifier, not the original identifier.
+     */
+    WebGLUniformInfo GetUniformInfoForMappedIdentifier(const nsACString& name);
+
+    WebGLContext *GetParentObject() const {
+        return Context();
+    }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLProgram)
+
+protected:
+
+    WebGLuint mGLName;
+    bool mLinkStatus;
+    // attached shaders of the program object
+    nsTArray<WebGLRefPtr<WebGLShader> > mAttachedShaders;
+    CheckedUint32 mGeneration;
+
+    // post-link data
+    nsTArray<bool> mAttribsInUse;
+    nsAutoPtr<CStringMap> mIdentifierMap, mIdentifierReverseMap;
+    nsAutoPtr<CStringToUniformInfoMap> mUniformInfoMap;
+    int mAttribMaxNameLength;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/canvas/src/WebGLUniformLocation.cpp
+++ b/content/canvas/src/WebGLUniformLocation.cpp
@@ -9,16 +9,26 @@
 using namespace mozilla;
 
 JSObject*
 WebGLUniformLocation::WrapObject(JSContext *cx, JSObject *scope)
 {
     return dom::WebGLUniformLocationBinding::Wrap(cx, scope, this);
 }
 
+WebGLUniformLocation::WebGLUniformLocation(WebGLContext *context, WebGLProgram *program, GLint location, const WebGLUniformInfo& info)
+    : WebGLContextBoundObject(context)
+    , mProgram(program)
+    , mProgramGeneration(program->Generation())
+    , mLocation(location)
+    , mInfo(info)
+{
+    mElementSize = info.ElementSize();
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebGLUniformLocation)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebGLUniformLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgram)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebGLUniformLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgram)
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLUniformLocation.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLUNIFORMLOCATION_H_
+#define WEBGLUNIFORMLOCATION_H_
+
+#include "WebGLObjectModel.h"
+
+namespace mozilla {
+
+class WebGLUniformLocation MOZ_FINAL
+    : public nsISupports
+    , public WebGLContextBoundObject
+{
+public:
+    WebGLUniformLocation(WebGLContext *context, WebGLProgram *program, GLint location, const WebGLUniformInfo& info);
+
+    ~WebGLUniformLocation() {
+    }
+
+    // needed for certain helper functions like ValidateObject.
+    // WebGLUniformLocation's can't be 'Deleted' in the WebGL sense.
+    bool IsDeleted() const { return false; }
+
+    const WebGLUniformInfo &Info() const { return mInfo; }
+
+    WebGLProgram *Program() const { return mProgram; }
+    GLint Location() const { return mLocation; }
+    uint32_t ProgramGeneration() const { return mProgramGeneration; }
+    int ElementSize() const { return mElementSize; }
+
+    virtual JSObject* WrapObject(JSContext *cx, JSObject *scope);
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(WebGLUniformLocation)
+
+protected:
+    // nsRefPtr, not WebGLRefPtr, so that we don't prevent the program from being explicitly deleted.
+    // we just want to avoid having a dangling pointer.
+    nsRefPtr<WebGLProgram> mProgram;
+
+    uint32_t mProgramGeneration;
+    GLint mLocation;
+    WebGLUniformInfo mInfo;
+    int mElementSize;
+    friend class WebGLProgram;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/canvas/test/webgl/Makefile.in
+++ b/content/canvas/test/webgl/Makefile.in
@@ -13,16 +13,17 @@ include $(DEPTH)/config/autoconf.mk
 MOCHITEST_FILES = \
   test_webgl_conformance_test_suite.html \
   00_test_list.txt \
   failing_tests_linux.txt \
   failing_tests_windows.txt \
   failing_tests_mac.txt \
   failing_tests_mac_mtnlion.txt \
   failing_tests_android.txt \
+  skipped_tests_android.txt \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(TAR) -cf - -C $(srcdir) \
 	  resources \
 	  conformance \
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -5452,23 +5452,35 @@ nsEventStateManager::WheelPrefs::Init(
 
   nsAutoCString prefNameZ(basePrefName);
   prefNameZ.AppendLiteral("delta_multiplier_z");
   mMultiplierZ[aIndex] =
     static_cast<double>(Preferences::GetInt(prefNameZ.get(), 100)) / 100;
 
   nsAutoCString prefNameAction(basePrefName);
   prefNameAction.AppendLiteral("action");
-  mActions[aIndex] =
-    static_cast<Action>(Preferences::GetInt(prefNameAction.get(),
-                                            ACTION_SCROLL));
-  if (mActions[aIndex] < ACTION_NONE || mActions[aIndex] > ACTION_LAST) {
+  int32_t action = Preferences::GetInt(prefNameAction.get(), ACTION_SCROLL);
+  if (action < ACTION_NONE || action > ACTION_LAST) {
     NS_WARNING("Unsupported action pref value, replaced with 'Scroll'.");
-    mActions[aIndex] = ACTION_SCROLL;
-  }
+    action = ACTION_SCROLL;
+  }
+  mActions[aIndex] = static_cast<Action>(action);
+
+  // Compute action values overridden by .override_x pref.
+  // At present, override is possible only for the x-direction
+  // because this pref is introduced mainly for tilt wheels.
+  prefNameAction.AppendLiteral(".override_x");
+  int32_t actionOverrideX = Preferences::GetInt(prefNameAction.get(), -1);
+  if (actionOverrideX < -1 || actionOverrideX > ACTION_LAST) {
+    NS_WARNING("Unsupported action override pref value, didn't override.");
+    actionOverrideX = -1;
+  }
+  mOverriddenActionsX[aIndex] = (actionOverrideX == -1)
+                              ? static_cast<Action>(action)
+                              : static_cast<Action>(actionOverrideX);
 }
 
 void
 nsEventStateManager::WheelPrefs::ApplyUserPrefsToDelta(
                                    widget::WheelEvent* aEvent)
 {
   Index index = GetIndexFor(aEvent);
   Init(index);
@@ -5519,31 +5531,35 @@ nsEventStateManager::WheelPrefs::Compute
 {
   if (!aEvent->deltaX && !aEvent->deltaY) {
     return ACTION_NONE;
   }
 
   Index index = GetIndexFor(aEvent);
   Init(index);
 
-  if (mActions[index] == ACTION_NONE || mActions[index] == ACTION_SCROLL) {
-    return mActions[index];
+  bool deltaXPreferred =
+    (std::abs(aEvent->deltaX) > std::abs(aEvent->deltaY) &&
+     std::abs(aEvent->deltaX) > std::abs(aEvent->deltaZ));
+  Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions;
+  if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL) {
+    return actions[index];
   }
 
   // Momentum events shouldn't run special actions.
   if (aEvent->isMomentum) {
     // Use the default action.  Note that user might kill the wheel scrolling.
     Init(INDEX_DEFAULT);
-    return (mActions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL :
-                                                        ACTION_NONE;
+    return (actions[INDEX_DEFAULT] == ACTION_SCROLL) ? ACTION_SCROLL :
+                                                       ACTION_NONE;
   }
 
   // If this event doesn't cause NS_MOUSE_SCROLL event or the direction is
   // oblique, history and zoom shouldn't be executed.
-  return !aEvent->GetPreferredIntDelta() ? ACTION_NONE : mActions[index];
+  return !aEvent->GetPreferredIntDelta() ? ACTION_NONE : actions[index];
 }
 
 bool
 nsEventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
                                    widget::WheelEvent* aEvent)
 {
   Index index = GetIndexFor(aEvent);
   Init(index);
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -345,17 +345,17 @@ protected:
      * CancelApplyingUserPrefsFromOverflowDelta() cancels the inflation.
      */
     void CancelApplyingUserPrefsFromOverflowDelta(
                                     mozilla::widget::WheelEvent* aEvent);
 
     /**
      * Computes the default action for the aEvent with the prefs.
      */
-    enum Action
+    enum Action MOZ_ENUM_TYPE(uint8_t)
     {
       ACTION_NONE = 0,
       ACTION_SCROLL,
       ACTION_HISTORY,
       ACTION_ZOOM,
       ACTION_LAST = ACTION_ZOOM
     };
     Action ComputeActionFor(mozilla::widget::WheelEvent* aEvent);
@@ -423,16 +423,22 @@ protected:
       MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000
     };
 
     bool mInit[COUNT_OF_MULTIPLIERS];
     double mMultiplierX[COUNT_OF_MULTIPLIERS];
     double mMultiplierY[COUNT_OF_MULTIPLIERS];
     double mMultiplierZ[COUNT_OF_MULTIPLIERS];
     Action mActions[COUNT_OF_MULTIPLIERS];
+    /**
+     * action values overridden by .override_x pref.
+     * If an .override_x value is -1, same as the
+     * corresponding mActions value.
+     */
+    Action mOverriddenActionsX[COUNT_OF_MULTIPLIERS];
 
     static WheelPrefs* sInstance;
   };
 
   /**
    * DeltaDirection is used for specifying whether the called method should
    * handle vertical delta or horizontal delta.
    * This is clearer than using bool.
--- a/content/events/test/window_wheel_default_action.html
+++ b/content/events/test/window_wheel_default_action.html
@@ -1526,16 +1526,176 @@ function doTestWholeScroll(aCallback)
       } else {
         doIt();
       }
     }, 20);
   }
   doIt();
 }
 
+function doTestActionOverride(aCallback)
+{
+  const kNoScroll    = 0x00;
+  const kScrollUp    = 0x01;
+  const kScrollDown  = 0x02;
+  const kScrollLeft  = 0x04;
+  const kScrollRight = 0x08;
+
+  const kTests = [
+    { action: 1, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 0, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 1, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 1, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 0, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 1.0, deltaY: 0.5,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 1, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kScrollDown | kScrollRight
+    },
+    { action: 0, override_x: -1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 0,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+    { action: 0, override_x: 1,
+      event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+               deltaX: 0.5, deltaY: 1.0,
+               lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+      expected: kNoScroll
+    },
+  ];
+
+  var index = 0;
+
+  function doIt()
+  {
+    const kTest = kTests[index];
+    description = "doTestActionOverride(action=" + kTest.action + ", " +
+                  "override_x=" + kTest.override_x + ", " +
+                  "deltaX=" + kTest.event.deltaX + ", " +
+                  "deltaY=" + kTest.event.deltaY + "): ";
+    SpecialPowers.setIntPref("mousewheel.default.action", kTest.action);
+    SpecialPowers.setIntPref("mousewheel.default.action.override_x", kTest.override_x);
+    gScrollableElement.scrollTop = 1000;
+    gScrollableElement.scrollLeft = 1000;
+    synthesizeWheel(gScrollableElement, 10, 10, kTest.event);
+    hitEventLoop(function () {
+      if (kTest.expected & kScrollUp) {
+        ok(gScrollableElement.scrollTop < 1000, description + "not scrolled up, got " + gScrollableElement.scrollTop);
+      } else if (kTest.expected & kScrollDown) {
+        ok(gScrollableElement.scrollTop > 1000, description + "not scrolled down, got " + gScrollableElement.scrollTop);
+      } else {
+        is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
+      }
+      if (kTest.expected & kScrollLeft) {
+        ok(gScrollableElement.scrollLeft < 1000, description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
+      } else if (kTest.expected & kScrollRight) {
+        ok(gScrollableElement.scrollLeft > 1000, description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
+      } else {
+        is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+      }
+      if (++index == kTests.length) {
+        SpecialPowers.setIntPref("mousewheel.default.action", 1);
+        SpecialPowers.clearUserPref("mousewheel.default.action.override_x");
+        SimpleTest.executeSoon(aCallback);
+      } else {
+        doIt();
+      }
+    }, 20);
+  }
+  doIt();
+}
+
 function runTests()
 {
   SpecialPowers.setBoolPref("general.smoothScroll", false);
 
   SpecialPowers.setIntPref("mousewheel.default.action", 1);      // scroll
   SpecialPowers.setIntPref("mousewheel.with_shift.action", 2);   // history
   SpecialPowers.setIntPref("mousewheel.with_control.action", 3); // zoom
 
@@ -1562,29 +1722,31 @@ function runTests()
       deltaMultiplierX:  1.0, deltaMultiplierY:  1.0, deltaMultiplierZ: -2.0 },
   ];
 
   var index = 0;
 
   function doTest() {
     setDeltaMultiplierSettings(kSettings[index]);
     doTestScroll(kSettings[index], function () {
-        doTestZoom(kSettings[index], function() {
-          if (++index == kSettings.length) {
-            setDeltaMultiplierSettings(kSettings[0]);
-            doTestZoomedScroll(function() {
-              doTestWholeScroll(function() {
+      doTestZoom(kSettings[index], function() {
+        if (++index == kSettings.length) {
+          setDeltaMultiplierSettings(kSettings[0]);
+          doTestZoomedScroll(function() {
+            doTestWholeScroll(function() {
+              doTestActionOverride(function() {
                 finishTests();
               });
             });
-          } else {
-            doTest();
-          }
-        });
+          });
+        } else {
+          doTest();
+        }
       });
+    });
   }
   doTest();
 }
 
 function finishTests()
 {
   SpecialPowers.clearUserPref("general.smoothScroll");
 
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -19,17 +19,17 @@ DOMCI_DATA(PropertyNodeList, mozilla::do
 
 namespace mozilla {
 namespace dom {
 
 static PLDHashOperator
 TraverseNamedProperties(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
 {
   nsCycleCollectionTraversalCallback* cb = static_cast<nsCycleCollectionTraversalCallback*>(aData);
-  cb->NoteXPCOMChild(static_cast<nsIDOMPropertyNodeList*>(aEntry));
+  cb->NoteXPCOMChild(static_cast<nsINodeList*>(aEntry));
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLPropertiesCollection)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
   tmp->SetDocument(nullptr);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
@@ -67,23 +67,21 @@ HTMLPropertiesCollection::~HTMLPropertie
 {
   if (mDoc) {
     mDoc->RemoveMutationObserver(this);
   }
 }
 
 NS_INTERFACE_TABLE_HEAD(HTMLPropertiesCollection)
     NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-    NS_INTERFACE_TABLE4(HTMLPropertiesCollection,
-                        nsIDOMHTMLPropertiesCollection,
+    NS_INTERFACE_TABLE3(HTMLPropertiesCollection,
                         nsIDOMHTMLCollection,
                         nsIHTMLCollection,
                         nsIMutationObserver)
     NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLPropertiesCollection)
-    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(HTMLPropertiesCollection)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLPropertiesCollection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLPropertiesCollection)
 
 
 static PLDHashOperator
 SetPropertyListDocument(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
@@ -173,31 +171,16 @@ HTMLPropertiesCollection::NamedItem(cons
     nsRefPtr<PropertyNodeList> newPropertyList =
       new PropertyNodeList(this, mRoot, aName);
     mNamedItemEntries.Put(aName, newPropertyList);
     propertyList = newPropertyList;
   }
   return propertyList;
 }
 
-NS_IMETHODIMP
-HTMLPropertiesCollection::NamedItem(const nsAString& aName,
-                                    nsIDOMPropertyNodeList** aResult)
-{
-  NS_ADDREF(*aResult = NamedItem(aName));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLPropertiesCollection::GetNames(nsIDOMDOMStringList** aResult)
-{
-  NS_ADDREF(*aResult = Names());
-  return NS_OK;
-}
-
 void
 HTMLPropertiesCollection::AttributeChanged(nsIDocument *aDocument, Element* aElement,
                                            int32_t aNameSpaceID, nsIAtom* aAttribute,
                                            int32_t aModType)
 {
   mIsDirty = true;
 }
 
@@ -277,52 +260,52 @@ HTMLPropertiesCollection::EnsureFresh()
       // ContainsInternal must not call EnsureFresh
       bool contains = mNames->ContainsInternal(propName);
       if (!contains) {
         mNames->Add(propName);
       }
     }
   }
 }
-  
+
 static Element*
 GetElementByIdForConnectedSubtree(nsIContent* aContent, const nsIAtom* aId)
 {
   aContent = static_cast<nsIContent*>(aContent->SubtreeRoot());
   do {
     if (aContent->GetID() == aId) {
       return aContent->AsElement();
     }
     aContent = aContent->GetNextNode();
   } while(aContent);
-  
+
   return NULL;
 }
 
 void
 HTMLPropertiesCollection::CrawlProperties()
 {
   nsIDocument* doc = mRoot->GetCurrentDoc();
- 
+
   const nsAttrValue* attr = mRoot->GetParsedAttr(nsGkAtoms::itemref);
   if (attr) {
     for (uint32_t i = 0; i < attr->GetAtomCount(); i++) {
       nsIAtom* ref = attr->AtomAt(i);
       Element* element;
       if (doc) {
         element = doc->GetElementById(nsDependentAtomString(ref));
       } else {
         element = GetElementByIdForConnectedSubtree(mRoot, ref);
       }
       if (element && element != mRoot) {
         CrawlSubtree(element);
       }
     }
   }
-  
+
   CrawlSubtree(mRoot);
 }
 
 void
 HTMLPropertiesCollection::CrawlSubtree(Element* aElement)
 {
   nsIContent* aContent = aElement;
   while (aContent) {
@@ -332,25 +315,25 @@ HTMLPropertiesCollection::CrawlSubtree(E
       // Move on to the next node in the tree
       aContent = aContent->GetNextNode(aElement);
     } else {
       MOZ_ASSERT(aContent->IsElement(), "IsHTML() returned true!");
       Element* element = aContent->AsElement();
       if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop) &&
           !mProperties.Contains(element)) {
         mProperties.AppendElement(static_cast<nsGenericHTMLElement*>(element));
-      }                 
-                     
+      }
+
       if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope)) {
         aContent = element->GetNextNonChildNode(aElement);
-      } else {          
+      } else {
         aContent = element->GetNextNode(aElement);
-      }                 
-    }                     
-  }                    
+      }
+    }
+  }
 }
 
 void
 HTMLPropertiesCollection::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   EnsureFresh();
   mNames->CopyList(aNames);
 }
@@ -455,23 +438,21 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Pro
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PropertyNodeList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PropertyNodeList)
 
 NS_INTERFACE_TABLE_HEAD(PropertyNodeList)
     NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-    NS_INTERFACE_TABLE4(PropertyNodeList,
-                        nsIDOMPropertyNodeList,
+    NS_INTERFACE_TABLE3(PropertyNodeList,
                         nsIDOMNodeList,
                         nsINodeList,
                         nsIMutationObserver)
     NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PropertyNodeList)
-    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(PropertyNodeList)
 NS_INTERFACE_MAP_END
 
 void
 PropertyNodeList::GetValues(JSContext* aCx, nsTArray<JS::Value >& aResult,
                             ErrorResult& aError)
 {
   EnsureFresh();
 
@@ -482,50 +463,16 @@ PropertyNodeList::GetValues(JSContext* a
     JS::Value v = mElements.ElementAt(i)->GetItemValue(aCx, wrapper, aError);
     if (aError.Failed()) {
       return;
     }
     aResult.AppendElement(v);
   }
 }
 
-NS_IMETHODIMP
-PropertyNodeList::GetValues(nsIVariant** aValues)
-{
-  EnsureFresh();
-  nsCOMPtr<nsIWritableVariant> out = new nsVariant();
-
-  // We have to use an nsTArray<nsIVariant*> here and do manual refcounting because 
-  // nsWritableVariant::SetAsArray takes an nsIVariant**.
-  nsTArray<nsIVariant*> values;
-
-  uint32_t length = mElements.Length();
-  if (length == 0) {
-    out->SetAsEmptyArray();
-  } else {
-    for (uint32_t i = 0; i < length; ++i) {
-      nsIVariant* itemValue;
-      mElements.ElementAt(i)->GetItemValue(&itemValue);
-      values.AppendElement(itemValue);
-    }
-    out->SetAsArray(nsIDataType::VTYPE_INTERFACE_IS,
-                    &NS_GET_IID(nsIVariant),
-                    values.Length(),
-                    values.Elements());
-  }
-
-  out.forget(aValues);
-
-  for (uint32_t i = 0; i < values.Length(); ++i) {
-    NS_RELEASE(values[i]);
-  }
-
-  return NS_OK;
-}
-
 void
 PropertyNodeList::AttributeChanged(nsIDocument* aDocument, Element* aElement,
                                    int32_t aNameSpaceID, nsIAtom* aAttribute,
                                    int32_t aModType)
 {
   mIsDirty = true;
 }
 
--- a/content/html/content/src/HTMLPropertiesCollection.h
+++ b/content/html/content/src/HTMLPropertiesCollection.h
@@ -5,29 +5,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HTMLPropertiesCollection_h_
 #define HTMLPropertiesCollection_h_
 
 #include "nsDOMLists.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
-#include "nsIDOMHTMLPropertiesCollection.h"
-#include "nsIDOMPropertyNodeList.h"
 #include "nsCOMArray.h"
 #include "nsIMutationObserver.h"
 #include "nsStubMutationObserver.h"
 #include "nsBaseHashtable.h"
 #include "nsINodeList.h"
 #include "nsIHTMLCollection.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
+#include "jsapi.h"
 
 class nsGenericHTMLElement;
-class nsXPCClassInfo;
 class nsIDocument;
 class nsINode;
 
 namespace mozilla {
 namespace dom {
 
 class HTMLPropertiesCollection;
 class PropertyNodeList;
@@ -43,33 +41,31 @@ public:
 
   bool ContainsInternal(const nsAString& aString);
 
 protected:
   nsRefPtr<HTMLPropertiesCollection> mCollection;
 };
 
 class HTMLPropertiesCollection : public nsIHTMLCollection,
-                                 public nsIDOMHTMLPropertiesCollection,
                                  public nsStubMutationObserver,
                                  public nsWrapperCache
 {
   friend class PropertyNodeList;
   friend class PropertyStringList;
 public:
   HTMLPropertiesCollection(nsGenericHTMLElement* aRoot);
   virtual ~HTMLPropertiesCollection();
 
   using nsWrapperCache::GetWrapperPreserveColor;
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap);
 
   virtual Element* GetElementAt(uint32_t aIndex);
 
-  NS_IMETHOD NamedItem(const nsAString& aName, nsIDOMNode** aResult);
   void SetDocument(nsIDocument* aDocument);
   nsINode* GetParentObject();
   virtual JSObject* NamedItem(JSContext* cx, const nsAString& name,
                               mozilla::ErrorResult& error);
   PropertyNodeList* NamedItem(const nsAString& aName);
   PropertyNodeList* NamedGetter(const nsAString& aName, bool& aFound)
   {
     aFound = IsSupportedNamedProperty(aName);
@@ -77,115 +73,113 @@ public:
   }
   nsDOMStringList* Names()
   {
     EnsureFresh();
     return mNames;
   }
   virtual void GetSupportedNames(nsTArray<nsString>& aNames);
 
+  NS_DECL_NSIDOMHTMLCOLLECTION
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIDOMHTMLPROPERTIESCOLLECTION
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(HTMLPropertiesCollection,
                                                          nsIHTMLCollection)
 
-  nsXPCClassInfo* GetClassInfo();
-
 protected:
   // Make sure this collection is up to date, in case the DOM has been mutated.
   void EnsureFresh();
-  
+
   // Crawl the properties of mRoot, following any itemRefs it may have
   void CrawlProperties();
 
   // Crawl startNode and its descendants, looking for items
   void CrawlSubtree(Element* startNode);
 
   bool IsSupportedNamedProperty(const nsAString& aName)
   {
     EnsureFresh();
     return mNames->ContainsInternal(aName);
   }
 
   // the items that make up this collection
-  nsTArray<nsRefPtr<nsGenericHTMLElement> > mProperties; 
-  
+  nsTArray<nsRefPtr<nsGenericHTMLElement> > mProperties;
+
   // the itemprop attribute of the properties
-  nsRefPtr<PropertyStringList> mNames; 
- 
-  // The cached PropertyNodeLists that are NamedItems of this collection 
+  nsRefPtr<PropertyStringList> mNames;
+
+  // The cached PropertyNodeLists that are NamedItems of this collection
   nsRefPtrHashtable<nsStringHashKey, PropertyNodeList> mNamedItemEntries;
-  
+
   // The element this collection is rooted at
   nsCOMPtr<nsGenericHTMLElement> mRoot;
-  
+
   // The document mRoot is in, if any
   nsCOMPtr<nsIDocument> mDoc;
-  
+
   // True if there have been DOM modifications since the last EnsureFresh call.
   bool mIsDirty;
 };
 
 class PropertyNodeList : public nsINodeList,
-                         public nsIDOMPropertyNodeList,
                          public nsStubMutationObserver
 {
 public:
   PropertyNodeList(HTMLPropertiesCollection* aCollection,
                    nsIContent* aRoot, const nsAString& aName);
   virtual ~PropertyNodeList();
 
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap);
 
   void SetDocument(nsIDocument* aDocument);
 
   void GetValues(JSContext* aCx, nsTArray<JS::Value >& aResult,
                  ErrorResult& aError);
 
   virtual nsIContent* Item(uint32_t aIndex);
-  NS_DECL_NSIDOMPROPERTYNODELIST
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(PropertyNodeList,
                                                          nsINodeList)
+  NS_DECL_NSIDOMNODELIST
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   // nsINodeList interface
   virtual int32_t IndexOf(nsIContent* aContent);
   virtual nsINode* GetParentObject();
-  
+
   void AppendElement(nsGenericHTMLElement* aElement)
   {
     mElements.AppendElement(aElement);
   }
-  
+
   void Clear()
   {
     mElements.Clear();
   }
-  
+
   void SetDirty() { mIsDirty = true; }
- 
+
 protected:
   // Make sure this list is up to date, in case the DOM has been mutated.
   void EnsureFresh();
 
-  // the the name that this list corresponds to 
+  // the the name that this list corresponds to
   nsString mName;
 
   // the document mParent is in, if any
   nsCOMPtr<nsIDocument> mDoc;
 
   // the collection that this list is a named item of
   nsRefPtr<HTMLPropertiesCollection> mCollection;
 
@@ -196,9 +190,9 @@ protected:
   nsTArray<nsRefPtr<nsGenericHTMLElement> > mElements;
 
   // True if there have been DOM modifications since the last EnsureFresh call. 
   bool mIsDirty;
 };
 
 } // namespace dom
 } // namespace mozilla
-#endif // HTMLPropertiesCollection_h_ 
+#endif // HTMLPropertiesCollection_h_
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -12,16 +12,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE		= content
 LIBRARY_NAME	= gkconhtmlcon_s
 LIBXUL_LIBRARY	= 1
 FAIL_ON_WARNINGS = 1
 
 
 EXPORTS		= \
+		HTMLPropertiesCollection.h \
 		nsGenericHTMLElement.h \
 		nsHTMLIFrameElement.h \
 		nsClientRect.h \
 		nsHTMLDNSPrefetch.h \
 		nsTimeRanges.h \
 		$(NULL)
 
 CPPSRCS		= \
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -4186,19 +4186,19 @@ nsGenericHTMLElement::Properties()
      properties = new HTMLPropertiesCollection(this);
      NS_ADDREF(properties);
      SetProperty(nsGkAtoms::microdataProperties, properties, HTMLPropertiesCollectionDestructor);
   }
   return properties;
 }
 
 void
-nsGenericHTMLElement::GetProperties(nsIDOMHTMLPropertiesCollection** aProperties)
+nsGenericHTMLElement::GetProperties(nsISupports** aProperties)
 {
-  NS_ADDREF(*aProperties = Properties());
+  NS_ADDREF(*aProperties = static_cast<nsIHTMLCollection*>(Properties()));
 }
 
 nsSize
 nsGenericHTMLElement::GetWidthHeightForImage(imgIRequest *aImageRequest)
 {
   nsSize size(0,0);
 
   nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -32,20 +32,20 @@ class nsPresState;
 class nsILayoutHistoryState;
 class nsIEditor;
 struct nsRect;
 struct nsSize;
 class nsHTMLFormElement;
 class nsIDOMHTMLMenuElement;
 class nsIDOMHTMLCollection;
 class nsDOMSettableTokenList;
-class nsIDOMHTMLPropertiesCollection;
 class nsIDOMDOMStringMap;
+
 namespace mozilla {
-namespace dom {
+namespace dom{
 class HTMLPropertiesCollection;
 }
 }
 
 typedef nsMappedAttributeElement nsGenericHTMLElementBase;
 
 /**
  * A common superclass for HTML elements
@@ -311,17 +311,17 @@ public:
   // nsIDOMHTMLElement methods. Note that these are non-virtual
   // methods, implementations are expected to forward calls to these
   // methods.
   NS_IMETHOD InsertAdjacentHTML(const nsAString& aPosition,
                                 const nsAString& aText);
   NS_IMETHOD GetItemValue(nsIVariant** aValue);
   NS_IMETHOD SetItemValue(nsIVariant* aValue);
 protected:
-  void GetProperties(nsIDOMHTMLPropertiesCollection** aProperties);
+  void GetProperties(nsISupports** aProperties);
   void GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu) const;
 
   // These methods are used to implement element-specific behavior of Get/SetItemValue
   // when an element has @itemprop but no @itemscope.
   virtual void GetItemValueText(nsAString& text);
   virtual void SetItemValueText(const nsAString& text);
   nsDOMSettableTokenList* GetTokenList(nsIAtom* aAtom);
   void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
@@ -1656,17 +1656,17 @@ protected:
     nsGenericHTMLElement::GetItemId(aId);                                      \
     return NS_OK;                                                              \
   }                                                                            \
   NS_IMETHOD SetItemId(const nsAString& aId) MOZ_FINAL {                       \
     mozilla::ErrorResult rv;                                                   \
     nsGenericHTMLElement::SetItemId(aId, rv);                                  \
     return rv.ErrorCode();                                                     \
   }                                                                            \
-  NS_IMETHOD GetProperties(nsIDOMHTMLPropertiesCollection** aReturn)           \
+  NS_IMETHOD GetProperties(nsISupports** aReturn)                              \
       MOZ_FINAL {                                                              \
     nsGenericHTMLElement::GetProperties(aReturn);                              \
     return NS_OK;                                                              \
   }                                                                            \
   NS_IMETHOD GetItemValue(nsIVariant** aValue) MOZ_FINAL {                     \
     return nsGenericHTMLElement::GetItemValue(aValue);                         \
   }                                                                            \
   NS_IMETHOD SetItemValue(nsIVariant* aValue) MOZ_FINAL {                      \
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -214,17 +214,17 @@ nsHTMLAnchorElement::HasDeferredDNSPrefe
   return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED);
 }
 
 nsresult
 nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                 nsIContent* aBindingParent,
                                 bool aCompileEventHandlers)
 {
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Prefetch links
   if (aDocument) {
@@ -252,17 +252,17 @@ nsHTMLAnchorElement::UnbindFromTree(bool
     UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
     // Possible that hostname could have changed since binding, but since this
     // covers common cases, most DNS prefetch requests will be canceled
     nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT);
   }
   
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
   
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     doc->UnregisterPendingLinkUpdate(this);
   }
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
@@ -461,17 +461,17 @@ nsHTMLAnchorElement::SetAttr(int32_t aNa
                                               aValue, aNotify);
 
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (reset) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
 nsHTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
                                bool aNotify)
@@ -480,17 +480,17 @@ nsHTMLAnchorElement::UnsetAttr(int32_t a
                                                 aNotify);
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, false);
   }
 
   return rv;
 }
 
 bool
 nsHTMLAnchorElement::ParseAttribute(int32_t aNamespaceID,
                                     nsIAtom* aAttribute,
--- a/content/html/content/src/nsHTMLAreaElement.cpp
+++ b/content/html/content/src/nsHTMLAreaElement.cpp
@@ -182,32 +182,32 @@ nsHTMLAreaElement::GetLinkTarget(nsAStri
   }
 }
 
 nsresult
 nsHTMLAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers)
 {
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
   if (aDocument) {
     aDocument->RegisterPendingLinkUpdate(this);
   }
   
   return nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
 }
 
 void
 nsHTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
   
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     doc->UnregisterPendingLinkUpdate(this);
   }
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
@@ -221,17 +221,17 @@ nsHTMLAreaElement::SetAttr(int32_t aName
     nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify);
 
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
 nsHTMLAreaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify)
@@ -240,17 +240,17 @@ nsHTMLAreaElement::UnsetAttr(int32_t aNa
                                                 aNotify);
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, false);
   }
 
   return rv;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   nsHTMLAreaElement::Get##_part(nsAString& a##_part)         \
--- a/content/html/content/src/nsHTMLLinkElement.cpp
+++ b/content/html/content/src/nsHTMLLinkElement.cpp
@@ -207,17 +207,17 @@ nsHTMLLinkElement::SetItemValueText(cons
   SetHref(aValue);
 }
 
 nsresult
 nsHTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers)
 {
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (aDocument) {
     aDocument->RegisterPendingLinkUpdate(this);
@@ -245,17 +245,17 @@ nsHTMLLinkElement::LinkRemoved()
   return NS_OK;
 }
 
 void
 nsHTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
 
   // Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
   // and so this messy event dispatch can go away.
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
   if (oldDoc) {
     oldDoc->UnregisterPendingLinkUpdate(this);
   }
   CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
@@ -317,17 +317,17 @@ nsHTMLLinkElement::SetAttr(int32_t aName
                                               aValue, aNotify);
 
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, true);
   }
 
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
       (aName == nsGkAtoms::href ||
        aName == nsGkAtoms::rel ||
        aName == nsGkAtoms::title ||
        aName == nsGkAtoms::media ||
        aName == nsGkAtoms::type)) {
@@ -365,17 +365,17 @@ nsHTMLLinkElement::UnsetAttr(int32_t aNa
   }
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, false);
   }
 
   return rv;
 }
 
 nsresult
 nsHTMLLinkElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
 {
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -264,16 +264,17 @@ MOCHITEST_FILES = \
 		test_bug742030.html \
 		test_bug742549.html \
 		test_bug745685.html \
 		test_input_file_picker.html \
 		test_bug763626.html \
 		test_bug780993.html \
 		test_bug786564.html \
 		test_bug797113.html \
+		test_bug787134.html \
 		test_iframe_sandbox_inheritance.html \
 		file_iframe_sandbox_a_if1.html \
 		file_iframe_sandbox_a_if2.html \
 		file_iframe_sandbox_a_if3.html \
 		file_iframe_sandbox_a_if4.html \
 		file_iframe_sandbox_a_if5.html \
 		file_iframe_sandbox_a_if6.html \
 		file_iframe_sandbox_a_if7.html \
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug787134.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=787134
+-->
+<head>
+  <title>Test for Bug 787134</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="reflect.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=787134">Mozilla Bug 787134</a>
+<p id="display"></p>
+<p><a id="link-test1" href="example link">example link</a></p>
+<pre id="test">
+<script>
+    var div = document.createElement('div');
+    div.innerHTML = '<a href=#></a>';
+    var a = div.firstChild;
+    ok(a.mozMatchesSelector(':link'), "Should match a link not in a document");
+    is(div.querySelector(':link'), a, "Should find a link not in a document");
+    a = document.querySelector('#link-test1');
+    ok(a.mozMatchesSelector(':link'), "Should match a link in a document with an invalid URL");
+</script>
+</pre>
+</body>
+</html>
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -43,17 +43,17 @@ NS_IMPL_RELEASE_INHERITED(nsMathMLElemen
 
 nsresult
 nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
   static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";
 
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     aDocument->RegisterPendingLinkUpdate(this);
@@ -78,17 +78,17 @@ nsMathMLElement::BindToTree(nsIDocument*
   return rv;
 }
 
 void
 nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
   
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     doc->UnregisterPendingLinkUpdate(this);
   }
 
   nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent);
 }
@@ -788,17 +788,17 @@ nsMathMLElement::SetAttr(int32_t aNameSp
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href &&
       (aNameSpaceID == kNameSpaceID_None ||
        aNameSpaceID == kNameSpaceID_XLink)) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
 nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
                            bool aNotify)
@@ -808,13 +808,15 @@ nsMathMLElement::UnsetAttr(int32_t aName
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttr == nsGkAtoms::href &&
       (aNameSpaceID == kNameSpaceID_None ||
        aNameSpaceID == kNameSpaceID_XLink)) {
-    Link::ResetLinkState(!!aNotify);
+    // Note: just because we removed a single href attr doesn't mean there's no href,
+    // since there are 2 possible namespaces.
+    Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
   }
 
   return rv;
 }
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1235,23 +1235,18 @@ nsresult MediaDecoder::GetSeekable(nsTim
   //TODO : change 0.0 to GetInitialTime() when available
   double initialTime = 0.0;
 
   // We can seek in buffered range if the media is seekable. Also, we can seek
   // in unbuffered ranges if the transport level is seekable (local file or the
   // server supports range requests, etc.)
   if (!IsMediaSeekable()) {
     return NS_OK;
-  } else if (!IsTransportSeekable()){
-    if (mDecoderStateMachine &&
-        mDecoderStateMachine->IsSeekableInBufferedRanges()) {
-      return GetBuffered(aSeekable);
-    } else {
-      return NS_OK;
-    }
+  } else if (!IsTransportSeekable()) {
+    return GetBuffered(aSeekable);
   } else {
     double end = IsInfinite() ? std::numeric_limits<double>::infinity()
                               : initialTime + GetDuration();
     aSeekable->Add(initialTime, end);
     return NS_OK;
   }
 }
 
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -417,19 +417,16 @@ protected:
 public:
   // Populates aBuffered with the time ranges which are buffered. aStartTime
   // must be the presentation time of the first frame in the media, e.g.
   // the media time corresponding to playback time/position 0. This function
   // should only be called on the main thread.
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered,
                                int64_t aStartTime) = 0;
 
-  // True if we can seek using only buffered ranges. This is backend dependant.
-  virtual bool IsSeekableInBufferedRanges() = 0;
-
   class VideoQueueMemoryFunctor : public nsDequeFunctor {
   public:
     VideoQueueMemoryFunctor() : mResult(0) {}
 
     virtual void* operator()(void* anObject);
 
     int64_t mResult;
   };
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -268,24 +268,16 @@ public:
     return mTransportSeekable;
   }
 
   bool IsMediaSeekable() {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     return mMediaSeekable;
   }
 
-  // Return true if the media is seekable using only buffered ranges.
-  bool IsSeekableInBufferedRanges() {
-    if (mReader) {
-      return mReader->IsSeekableInBufferedRanges();
-    }
-    return false;
-  }
-
   // Sets the current frame buffer length for the MozAudioAvailable event.
   // Accessed on the main and state machine threads.
   void SetFrameBufferLength(uint32_t aLength);
 
   // Returns the shared state machine thread.
   static nsIThread* GetStateMachineThread();
 
   // Schedules the shared state machine thread to run the state machine.
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -582,29 +582,32 @@ MediaStreamGraphImpl::UpdateConsumptionS
 void
 MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
                                           GraphTime aDesiredUpToTime,
                                           bool* aEnsureNextIteration)
 {
   bool finished;
   {
     MutexAutoLock lock(aStream->mMutex);
-    if (aStream->mPullEnabled && !aStream->mFinished) {
-      for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
-        MediaStreamListener* l = aStream->mListeners[j];
-        {
-          // Compute how much stream time we'll need assuming we don't block
-          // the stream at all between mBlockingDecisionsMadeUntilTime and
-          // aDesiredUpToTime.
-          StreamTime t =
-            GraphTimeToStreamTime(aStream, mStateComputedTime) +
-            (aDesiredUpToTime - mStateComputedTime);
-          MutexAutoUnlock unlock(aStream->mMutex);
-          l->NotifyPull(this, t);
-          *aEnsureNextIteration = true;
+    if (aStream->mPullEnabled && !aStream->mFinished &&
+        !aStream->mListeners.IsEmpty()) {
+      // Compute how much stream time we'll need assuming we don't block
+      // the stream at all between mBlockingDecisionsMadeUntilTime and
+      // aDesiredUpToTime.
+      StreamTime t =
+        GraphTimeToStreamTime(aStream, mStateComputedTime) +
+        (aDesiredUpToTime - mStateComputedTime);
+      if (t > aStream->mBuffer.GetEnd()) {
+        *aEnsureNextIteration = true;
+        for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
+          MediaStreamListener* l = aStream->mListeners[j];
+          {
+            MutexAutoUnlock unlock(aStream->mMutex);
+            l->NotifyPull(this, t);
+          }
         }
       }
     }
     finished = aStream->mUpdateFinished;
     for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
       SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
       for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
         MediaStreamListener* l = aStream->mListeners[j];
--- a/content/media/dash/DASHReader.cpp
+++ b/content/media/dash/DASHReader.cpp
@@ -362,27 +362,16 @@ MediaQueue<VideoData>&
 DASHReader::VideoQueue()
 {
   ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
                                          mDecoder->GetReentrantMonitor());
   NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
   return mVideoQueue;
 }
 
-bool
-DASHReader::IsSeekableInBufferedRanges()
-{
-  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
-                                         mDecoder->GetReentrantMonitor());
-  // At least one subreader must exist, and all subreaders must return true.
-  return (mVideoReader || mAudioReader) &&
-          !((mVideoReader && !mVideoReader->IsSeekableInBufferedRanges()) ||
-            (mAudioReader && !mAudioReader->IsSeekableInBufferedRanges()));
-}
-
 void
 DASHReader::RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
                                        uint32_t aToReaderIdx,
                                        uint32_t aSubsegmentIdx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(aFromReaderIdx < mVideoReaders.Length(),
                "From index is greater than number of video readers!");
--- a/content/media/dash/DASHReader.h
+++ b/content/media/dash/DASHReader.h
@@ -110,19 +110,16 @@ public:
                 int64_t aCurrentTime);
 
   // Called by state machine on multiple threads.
   nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
 
   // Called on the state machine or decode threads.
   VideoData* FindStartTime(int64_t& aOutStartTime);
 
-  // Call by state machine on multiple threads.
-  bool IsSeekableInBufferedRanges();
-
   // Prepares for an upcoming switch of video readers. Called by
   // |DASHDecoder| when it has switched download streams. Sets the index of
   // the reader to switch TO and the index of the subsegment to switch AT
   // (start offset). (Note: Subsegment boundaries are switch access points for
   // DASH-WebM). Called on the main thread. Must be in the decode monitor.
   void RequestVideoReaderSwitch(uint32_t aFromReaderIdx,
                                 uint32_t aToReaderIdx,
                                 uint32_t aSubsegmentIdx);
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -31,20 +31,16 @@ public:
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
 
-  virtual bool IsSeekableInBufferedRanges() {
-    return true;
-  }
-
   virtual bool HasAudio() {
     return mInfo.mHasAudio;
   }
 
   virtual bool HasVideo() {
     return mInfo.mHasVideo;
   }
 
--- a/content/media/ogg/OggReader.h
+++ b/content/media/ogg/OggReader.h
@@ -46,22 +46,16 @@ public:
     return mTheoraState != 0 && mTheoraState->mActive;
   }
 
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
 
-  // We use bisection to seek in buffered range, but we don't allow seeking in a
-  // chained ogg file.
-  virtual bool IsSeekableInBufferedRanges() {
-    return true;
-  }
-
 private:
   // This monitor should be taken when reading or writing to mIsChained.
   ReentrantMonitor mMonitor;
 
   // Specialized Reset() method to signal if the seek is
   // to the start of the stream.
   nsresult ResetDecode(bool start);
 
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -49,17 +49,13 @@ public:
   {
     return mHasVideo;
   }
 
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
-  virtual bool IsSeekableInBufferedRanges() {
-    return true;
-  }
-
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/plugins/MediaPluginReader.h
+++ b/content/media/plugins/MediaPluginReader.h
@@ -49,17 +49,13 @@ public:
   {
     return mHasVideo;
   }
 
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
-  virtual bool IsSeekableInBufferedRanges() {
-    return true;
-  }
-
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/raw/RawReader.h
+++ b/content/media/raw/RawReader.h
@@ -34,21 +34,16 @@ public:
     return true;
   }
 
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
 
-  // By seeking in the media resource, it is possible to seek.
-  bool IsSeekableInBufferedRanges() {
-    return true;
-  }
-
 private:
   bool ReadFromResource(MediaResource *aResource, uint8_t *aBuf, uint32_t aLength);
 
   RawVideoHeader mMetadata;
   uint32_t mCurrentFrame;
   double mFrameRate;
   uint32_t mFrameSize;
   nsIntRect mPicture;
--- a/content/media/webm/WebMReader.cpp
+++ b/content/media/webm/WebMReader.cpp
@@ -401,16 +401,22 @@ nsresult WebMReader::ReadMetadata(VideoI
         done = true;
       } else {
         clusterNum++;
       }
     } while (!done);
   }
 #endif
 
+  // We can't seek in buffered regions if we have no cues.
+  bool haveCues;
+  int64_t dummy = -1;
+  haveCues = nestegg_get_cue_point(mContext, 0, -1, &dummy, &dummy) == 0;
+  mDecoder->SetMediaSeekable(haveCues);
+
   *aInfo = mInfo;
 
   *aTags = nullptr;
 
 #ifdef MOZ_DASH
   mDecoder->OnReadMetadataCompleted();
 #endif
 
--- a/content/media/webm/WebMReader.h
+++ b/content/media/webm/WebMReader.h
@@ -128,21 +128,16 @@ public:
   }
 
   virtual bool HasVideo()
   {
     NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
     return mHasVideo;
   }
 
-  // Bug 575140, cannot seek in webm if no cue is present.
-  bool IsSeekableInBufferedRanges() {
-    return false;
-  }
-
   virtual nsresult ReadMetadata(VideoInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
 
 #ifdef MOZ_DASH
   virtual void SetMainReader(DASHReader *aMainReader) MOZ_OVERRIDE {
--- a/content/svg/content/src/nsSVGAElement.cpp
+++ b/content/svg/content/src/nsSVGAElement.cpp
@@ -105,17 +105,17 @@ nsSVGAElement::GetTarget(nsIDOMSVGAnimat
 //----------------------------------------------------------------------
 // nsIContent methods
 
 nsresult
 nsSVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                           nsIContent *aBindingParent,
                           bool aCompileEventHandlers)
 {
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsSVGAElementBase::BindToTree(aDocument, aParent,
                                               aBindingParent,
                                               aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (aDocument) {
     aDocument->RegisterPendingLinkUpdate(this);
@@ -124,17 +124,17 @@ nsSVGAElement::BindToTree(nsIDocument *a
   return NS_OK;
 }
 
 void
 nsSVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
-  Link::ResetLinkState(false);
+  Link::ResetLinkState(false, Link::ElementHasHref());
   
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     doc->UnregisterPendingLinkUpdate(this);
   }
 
   nsSVGAElementBase::UnbindFromTree(aDeep, aNullParent);
 }
@@ -277,17 +277,17 @@ nsSVGAElement::SetAttr(int32_t aNameSpac
                                            aValue, aNotify);
 
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_XLink) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
 nsSVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
                          bool aNotify)
@@ -295,17 +295,17 @@ nsSVGAElement::UnsetAttr(int32_t aNameSp
   nsresult rv = nsSVGAElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttr == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_XLink) {
-    Link::ResetLinkState(!!aNotify);
+    Link::ResetLinkState(!!aNotify, false);
   }
 
   return rv;
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8977,33 +8977,34 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     // XXXbz mTiming should know what channel it's for, so we don't
     // need this hackery.  Note that this is still broken in cases
     // when we're loading something that's not javascript: and the
     // beforeunload handler denies the load.  That will screw up
     // timing for the next load!
     if (!bIsJavascript) {
         MaybeInitTiming();
     }
-    if (mTiming) {
+    bool timeBeforeUnload = mTiming && aFileName.IsVoid();
+    if (timeBeforeUnload) {
       mTiming->NotifyBeforeUnload();
     }
     // Check if the page doesn't want to be unloaded. The javascript:
     // protocol handler deals with this for javascript: URLs.
-    if (!bIsJavascript && mContentViewer) {
+    if (!bIsJavascript && aFileName.IsVoid() && mContentViewer) {
         bool okToUnload;
         rv = mContentViewer->PermitUnload(false, &okToUnload);
 
         if (NS_SUCCEEDED(rv) && !okToUnload) {
             // The user chose not to unload the page, interrupt the
             // load.
             return NS_OK;
         }
     }
 
-    if (mTiming) {
+    if (timeBeforeUnload) {
       mTiming->NotifyUnloadAccepted(mCurrentURI);
     }
 
     // Check for saving the presentation here, before calling Stop().
     // This is necessary so that we can catch any pending requests.
     // Since the new request has not been created yet, we pass null for the
     // new request parameter.
     // Also pass nullptr for the document, since it doesn't affect the return
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -7,16 +7,21 @@
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
+  return Cc["@mozilla.org/network/util;1"]
+           .getService(Ci.nsINetUtil);
+});
+
 // Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js
 
 this.EXPORTED_SYMBOLS = ["AppsUtils", "ManifestHelper"];
 
 function debug(s) {
   //dump("-*- AppsUtils.jsm: " + s + "\n");
 }
 
@@ -180,18 +185,21 @@ this.AppsUtils = {
         return false;
       }
     }
 
     return true;
   },
 
   checkManifestContentType: function
-     checkManifestContentType(installOrigin, webappOrigin, contentType) {
-    if (installOrigin != webappOrigin &&
+     checkManifestContentType(aInstallOrigin, aWebappOrigin, aContentType) {
+    let hadCharset = { };
+    let charset = { };
+    let contentType = NetUtil.parseContentType(aContentType, charset, hadCharset);
+    if (aInstallOrigin != aWebappOrigin &&
         contentType != "application/x-web-app-manifest+json") {
       return false;
     }
     return true;
   },
 
   /**
    * Determines whether the manifest allows installs for the given origin.
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -68,19 +68,19 @@ this.PermissionsTable =  { geolocation: 
                            contacts: {
                              app: DENY_ACTION,
                              privileged: PROMPT_ACTION,
                              certified: ALLOW_ACTION,
                              access: ["read", "write", "create"]
                            },
                            "device-storage:apps": {
                              app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
+                             privileged: DENY_ACTION,
                              certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
+                             access: ["read"]
                            },
                            "device-storage:pictures": {
                              app: DENY_ACTION,
                              privileged: PROMPT_ACTION,
                              certified: ALLOW_ACTION,
                              access: ["read", "write", "create"]
                            },
                            "device-storage:videos": {
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -153,17 +153,17 @@ this.DOMApplicationRegistry = {
 #ifdef MOZ_SYS_MSG
     this._processManifestForIds(ids, aRunUpdate);
 #else
     // Read the CSPs. If MOZ_SYS_MSG is defined this is done on
     // _processManifestForIds so as to not reading the manifests
     // twice
     this._readManifests(ids, (function readCSPs(aResults) {
       aResults.forEach(function registerManifest(aResult) {
-        this.webapps[aResult.id].csp = manifest.csp || "";
+        this.webapps[aResult.id].csp = aResult.manifest.csp || "";
       }, this);
     }).bind(this));
 
     // Nothing else to do but notifying we're ready.
     this.notifyAppsRegistryReady();
 #endif
   },
 
@@ -1408,17 +1408,20 @@ this.DOMApplicationRegistry = {
       // was cancelled via cancelDownload, which already sends its own
       // notification.
       if (!app.downloading && !app.downloadAvailable && !app.downloadSize) {
         return;
       }
 
       let download = self.downloads[aApp.manifestURL];
       app.downloading = false;
-      app.installState = download.previousState;
+      // If there were not enough storage to download the packaged app we
+      // won't have a record of the download details, so we just set the
+      // installState to 'pending'.
+      app.installState = download ? download.previousState : "pending";
       self.broadcastMessage("Webapps:PackageEvent",
                             { type: "error",
                               manifestURL:  aApp.manifestURL,
                               error: aError,
                               app: app });
     }
 
     function getInferedStatus() {
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -117,16 +117,32 @@ AudioChannelService::UnregisterAudioChan
 }
 
 void
 AudioChannelService::UnregisterType(AudioChannelType aType)
 {
   mChannelCounters[aType]--;
   MOZ_ASSERT(mChannelCounters[aType] >= 0);
 
+  bool isNoChannelUsed = true;
+  for (int32_t type = AUDIO_CHANNEL_NORMAL;
+         type <= AUDIO_CHANNEL_PUBLICNOTIFICATION;
+         ++type) {
+    if (mChannelCounters[type]) {
+      isNoChannelUsed = false;
+      break;
+    }
+  }
+
+  if (isNoChannelUsed) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->NotifyObservers(nullptr, "audio-channel-changed", NS_LITERAL_STRING("default").get());
+    return;
+  }
+
   // In order to avoid race conditions, it's safer to notify any existing
   // agent any time a new one is registered.
   Notify();
 }
 
 bool
 AudioChannelService::GetMuted(AudioChannelType aType, bool aElementHidden)
 {
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -95,17 +95,16 @@
 #include "nsPIDOMWindow.h"
 #include "nsIDOMJSWindow.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsIDOMHistory.h"
 #include "nsIDOMMediaList.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIDOMConstructor.h"
 #include "nsClientRect.h"
-#include "nsIDOMHTMLPropertiesCollection.h"
 
 // DOM core includes
 #include "nsError.h"
 #include "nsIDOMDOMException.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNamedNodeMap.h"
 #include "nsIDOMDOMStringList.h"
 #include "nsIDOMDOMTokenList.h"
@@ -309,18 +308,16 @@
 #include "nsIXULTemplateBuilder.h"
 #include "nsTreeColumns.h"
 #endif
 #include "nsIDOMXPathExpression.h"
 #include "nsIDOMNSXPathExpression.h"
 #include "nsIDOMXPathNSResolver.h"
 #include "nsIDOMXPathResult.h"
 #include "nsIDOMMozBrowserFrame.h"
-#include "nsIDOMHTMLPropertiesCollection.h"
-#include "nsIDOMPropertyNodeList.h"
 
 #include "nsIDOMGetSVGDocument.h"
 #include "nsIDOMSVGAElement.h"
 #include "nsIDOMSVGAltGlyphElement.h"
 #include "nsIDOMSVGAngle.h"
 #include "nsIDOMSVGAnimatedAngle.h"
 #include "nsIDOMSVGAnimatedBoolean.h"
 #include "nsIDOMSVGAnimatedEnum.h"
@@ -867,22 +864,16 @@ static nsDOMClassInfoData sClassInfoData
   // Misc HTML classes
   NS_DEFINE_CLASSINFO_DATA(HTMLDocument, nsHTMLDocumentSH,
                            DOCUMENT_SCRIPTABLE_FLAGS |
                            nsIXPCScriptable::WANT_GETPROPERTY)
   NS_DEFINE_CLASSINFO_DATA(HTMLOptionsCollection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(HTMLCollection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(HTMLPropertiesCollection, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(PropertyNodeList, 
-                           nsDOMGenericSH, 
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   // HTML element classes
   NS_DEFINE_CLASSINFO_DATA(HTMLElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(HTMLAnchorElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(HTMLAppletElement, nsHTMLPluginObjElementSH,
                            EXTERNAL_OBJ_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(HTMLAreaElement, nsElementSH,
@@ -2676,26 +2667,16 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLOptionsCollection)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLCollection)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(HTMLCollection, nsIDOMHTMLCollection)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLCollection)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(HTMLPropertiesCollection, nsIDOMHTMLPropertiesCollection)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLPropertiesCollection)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLCollection)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN(PropertyNodeList, nsIDOMPropertyNodeList)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMPropertyNodeList)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeList)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(HTMLElement, nsIDOMHTMLElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLElement)
     DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(HTMLAnchorElement, nsIDOMHTMLAnchorElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLAnchorElement)
     DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
@@ -9558,42 +9539,32 @@ nsHTMLSelectElementSH::SetProperty(nsIXP
 
 // HTMLObject/EmbedElement helper
 // Keep in mind that it is OK for this to fail to return an instance. Don't return a
 // failure result unless something truly exceptional has happened.
 // static
 nsresult
 nsHTMLPluginObjElementSH::GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *wrapper,
                                                   JSObject *obj,
+                                                  JSContext *cx,
                                                   nsNPAPIPluginInstance **_result)
 {
   *_result = nullptr;
 
   nsCOMPtr<nsIContent> content(do_QueryWrappedNative(wrapper, obj));
   NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(content));
   NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent");
 
-  nsresult rv = objlc->GetPluginInstance(_result);
-  if (NS_SUCCEEDED(rv) && *_result) {
-    return rv;
-  }
-
-  // If it's not safe to run script we'll only return the instance if it exists.
-  // Ditto if the document is inactive.
-  if (!nsContentUtils::IsSafeToRunScript() || !content->OwnerDoc()->IsActive()) {
-    return rv;
-  }
-
-  // We don't care if this actually starts the plugin or not, we just want to
-  // try to start it now if possible.
-  objlc->SyncStartPluginInstance();
-
-  return objlc->GetPluginInstance(_result);
+  bool callerIsContentJS = (!xpc::AccessCheck::callerIsChrome() &&
+                            !xpc::AccessCheck::callerIsXBL(cx) &&
+                            js::IsContextRunningJS(cx));
+  return objlc->ScriptRequestPluginInstance(callerIsContentJS,
+                                            _result);
 }
 
 class nsPluginProtoChainInstallRunner MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_ISUPPORTS
 
   nsPluginProtoChainInstallRunner(nsIXPConnectWrappedNative* wrapper,
@@ -9644,17 +9615,17 @@ nsHTMLPluginObjElementSH::SetupProtoChai
   if (!cxPusher.Push(cx)) {
     return NS_OK;
   }
 
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, obj);
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
-  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, cx, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!pi) {
     // No plugin around for this object.
 
     return NS_OK;
   }
 
@@ -9850,17 +9821,17 @@ nsHTMLPluginObjElementSH::SetProperty(ns
 }
 
 NS_IMETHODIMP
 nsHTMLPluginObjElementSH::Call(nsIXPConnectWrappedNative *wrapper,
                                JSContext *cx, JSObject *obj, uint32_t argc,
                                jsval *argv, jsval *vp, bool *_retval)
 {
   nsRefPtr<nsNPAPIPluginInstance> pi;
-  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, cx, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // If obj is a native wrapper, or if there's no plugin around for
   // this object, throw.
   if (ObjectIsNativeWrapper(cx, obj) || !pi) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -9917,17 +9888,17 @@ nsHTMLPluginObjElementSH::NewResolve(nsI
                                      JSContext *cx, JSObject *obj, jsid id,
                                      uint32_t flags, JSObject **objp,
                                      bool *_retval)
 {
   // Make sure the plugin instance is loaded and instantiated, if
   // possible.
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
-  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, obj, cx, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return nsElementSH::NewResolve(wrapper, cx, obj, id, flags, objp,
                                  _retval);
 }
  
 // Plugin helper
 
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -843,16 +843,17 @@ protected:
   }
 
   virtual ~nsHTMLPluginObjElementSH()
   {
   }
 
   static nsresult GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *aWrapper,
                                           JSObject *obj,
+                                          JSContext *cx,
                                           nsNPAPIPluginInstance **aResult);
 
   static nsresult GetPluginJSObject(JSContext *cx, JSObject *obj,
                                     nsNPAPIPluginInstance *plugin_inst,
                                     JSObject **plugin_obj,
                                     JSObject **plugin_proto);
 
 public:
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -49,18 +49,16 @@ DOMCI_CLASS(CompositionEvent)
 DOMCI_CLASS(DeviceMotionEvent)
 DOMCI_CLASS(DeviceAcceleration)
 DOMCI_CLASS(DeviceRotationRate)
 
 // HTML classes
 DOMCI_CLASS(HTMLDocument)
 DOMCI_CLASS(HTMLOptionsCollection)
 DOMCI_CLASS(HTMLCollection)
-DOMCI_CLASS(HTMLPropertiesCollection)
-DOMCI_CLASS(PropertyNodeList)
 
 // HTML element classes
 DOMCI_CLASS(HTMLElement)
 DOMCI_CLASS(HTMLAnchorElement)
 DOMCI_CLASS(HTMLAppletElement)
 DOMCI_CLASS(HTMLAreaElement)
 DOMCI_CLASS(HTMLBRElement)
 DOMCI_CLASS(HTMLBaseElement)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -145,16 +145,18 @@
 #include "nsIControllerContext.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsCSSProps.h"
 #include "nsIDOMFile.h"
 #include "nsIDOMFileList.h"
 #include "nsIURIFixup.h"
+#include "nsIAppStartup.h"
+#include "nsToolkitCompsCID.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsEventDispatcher.h"
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsNetUtil.h"
 #include "nsFocusManager.h"
 #include "nsIXULWindow.h"
 #include "nsEventStateManager.h"
@@ -6928,19 +6930,29 @@ public:
       nsCOMPtr<nsISupportsPRUint64> wrapper =
         do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
       if (wrapper) {
         wrapper->SetData(mID);
         observerService->NotifyObservers(wrapper, mTopic.get(), nullptr);
       }
     }
 
+    bool skipNukeCrossCompartment = false;
+#ifndef DEBUG
+    nsCOMPtr<nsIAppStartup> appStartup =
+      do_GetService(NS_APPSTARTUP_CONTRACTID);
+
+    if (appStartup) {
+      appStartup->GetShuttingDown(&skipNukeCrossCompartment);
+    }
+#endif
+
     nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
-    if (window) {
-      nsGlobalWindow* currentInner = 
+    if (!skipNukeCrossCompartment && window) {
+      nsGlobalWindow* currentInner =
         window->IsInnerWindow() ? static_cast<nsGlobalWindow*>(window.get()) :
                                   static_cast<nsGlobalWindow*>(window->GetCurrentInnerWindow());
       NS_ENSURE_TRUE(currentInner, NS_OK);
 
       JSObject* obj = currentInner->FastGetGlobalJSObject();
       // We only want to nuke wrappers for the chrome->content case
       if (obj && !js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
         JSContext* cx =
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -366,29 +366,30 @@ nsScreen::MozLockOrientation(const Seque
   }
 
   switch (GetLockOrientationPermission()) {
     case LOCK_DENIED:
       return false;
     case LOCK_ALLOWED:
       return hal::LockScreenOrientation(orientation);
     case FULLSCREEN_LOCK_ALLOWED: {
+      // We need to register a listener so we learn when we leave full-screen
+      // and when we will have to unlock the screen.
+      // This needs to be done before LockScreenOrientation call to make sure
+      // the locking can be unlocked.
+      nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+      if (!target) {
+        return false;
+      }
+
       if (!hal::LockScreenOrientation(orientation)) {
         return false;
       }
 
       // We are fullscreen and lock has been accepted.
-      // Now, we need to register a listener so we learn when we leave
-      // full-screen and when we will have to unlock the screen.
-      nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-      if (!target) {
-        // XXX: Bug 796873
-        return true;
-      }
-
       if (!mEventListener) {
         mEventListener = new FullScreenEventListener();
       }
 
       aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
                                            mEventListener, /* useCapture = */ true);
       return true;
     }
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -150,26 +150,29 @@ public:
   };
 
 private:
   nsCOMPtr<nsIInputStream> mInputStream;
 };
 
 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
                                            , mConnectionId(1)
-                                           , mLastCommand(0)
                                            , mRemoteObexVersion(0)
                                            , mRemoteConnectionFlags(0)
                                            , mRemoteMaxPacketLength(0)
-                                           , mAbortFlag(false)
+                                           , mLastCommand(0)
                                            , mPacketLeftLength(0)
-                                           , mPutFinal(false)
+                                           , mBodySegmentLength(0)
+                                           , mReceivedDataBufferOffset(0)
+                                           , mAbortFlag(false)
+                                           , mNewFileFlag(false)
+                                           , mPutFinalFlag(false)
+                                           , mSendTransferCompleteFlag(false)
+                                           , mSuccessFlag(false)
                                            , mWaitingForConfirmationFlag(false)
-                                           , mReceivedDataBufferOffset(0)
-                                           , mBodySegmentLength(0)
 {
   mConnectedDeviceAddress.AssignLiteral("00:00:00:00:00:00");
   mSocketStatus = GetConnectionStatus();
 }
 
 BluetoothOppManager::~BluetoothOppManager()
 {
 }
@@ -355,38 +358,54 @@ BluetoothOppManager::ConfirmReceivingFil
     NS_WARNING("We are not waiting for a confirmation now.");
     return false;
   }
   mWaitingForConfirmationFlag = false;
 
   NS_ASSERTION(mPacketLeftLength == 0,
                "Should not be in the middle of receiving a PUT packet.");
 
+  // For the first packet of first file
   bool success = false;
   if (aConfirm) {
     StartFileTransfer();
-    success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
+    if (CreateFile()) {
+      success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
+    }
+  }
+
+  if (success && mPutFinalFlag) {
+    mSuccessFlag = true;
+    FileTransferComplete();
   }
 
-  ReplyToPut(mPutFinal, success);
+  ReplyToPut(mPutFinalFlag, success);
   return true;
 }
 
 void
+BluetoothOppManager::AfterFirstPut()
+{
+  mUpdateProgressCounter = 1;
+  mPutFinalFlag = false;
+  mReceivedDataBufferOffset = 0;
+  mSendTransferCompleteFlag = false;
+  sSentFileLength = 0;
+  mSuccessFlag = false;
+}
+
+void
 BluetoothOppManager::AfterOppConnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = true;
-  mUpdateProgressCounter = 1;
-  sSentFileLength = 0;
-  mReceivedDataBufferOffset = 0;
   mAbortFlag = false;
-  mSuccessFlag = false;
   mWaitingForConfirmationFlag = true;
+  AfterFirstPut();
 }
 
 void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
@@ -487,96 +506,286 @@ BluetoothOppManager::WriteToFile(const u
     return false;
   }
 
   return true;
 }
 
 // Virtual function of class SocketConsumer
 void
-BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
+BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
+{
+  if (aHeader.Has(ObexHeaderId::Name)) {
+    aHeader.GetName(sFileName);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Type)) {
+    aHeader.GetContentType(sContentType);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Length)) {
+    aHeader.GetLength(&sFileLength);
+  }
+
+  if (aHeader.Has(ObexHeaderId::Body) ||
+      aHeader.Has(ObexHeaderId::EndOfBody)) {
+    uint8_t* bodyPtr;
+    aHeader.GetBody(&bodyPtr);
+    mBodySegment = bodyPtr;
+
+    aHeader.GetBodyLength(&mBodySegmentLength);
+  }
+}
+
+bool
+BluetoothOppManager::ExtractBlobHeaders()
+{
+  nsresult rv = mBlob->GetType(sContentType);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Can't get content type");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  uint64_t fileLength;
+  rv = mBlob->GetSize(&fileLength);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Can't get file size");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  // Currently we keep the size of files which were sent/received via
+  // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
+  // is only 4-byte long. Although it is possible to transfer a file
+  // larger than UINT32_MAX, it needs to parse another OBEX Header
+  // and I would like to leave it as a feature.
+  if (fileLength > (uint64_t)UINT32_MAX) {
+    NS_WARNING("The file size is too large for now");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  sFileLength = fileLength;
+  rv = NS_NewThread(getter_AddRefs(mReadFileThread));
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Can't create thread");
+    SendDisconnectRequest();
+    return false;
+  }
+
+  return true;
+}
+
+void
+BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
 {
   uint8_t opCode;
   int packetLength;
   int receivedLength = aMessage->mSize;
 
   if (mPacketLeftLength > 0) {
-    opCode = mPutFinal ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
+    opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
+    packetLength = mPacketLeftLength;
+  } else {
+    opCode = aMessage->mData[0];
+    packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
+
+    // When there's a Put packet right after a PutFinal packet,
+    // which means it's the start point of a new file.
+    if (mPutFinalFlag &&
+        (opCode == ObexRequestCode::Put || opCode == ObexRequestCode::PutFinal)) {
+      mNewFileFlag = true;
+      AfterFirstPut();
+    }
+  }
+
+  ObexHeaderSet pktHeaders(opCode);
+  if (opCode == ObexRequestCode::Connect) {
+    // Section 3.3.1 "Connect", IrOBEX 1.2
+    // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
+    // [Headers:var]
+    ParseHeaders(&aMessage->mData[7],
+                 receivedLength - 7,
+                 &pktHeaders);
+    ReplyToConnect();
+    AfterOppConnected();
+    mTransferMode = true;
+  } else if (opCode == ObexRequestCode::Disconnect ||
+             opCode == ObexRequestCode::Abort) {
+    // Section 3.3.2 "Disconnect", IrOBEX 1.2
+    // Section 3.3.5 "Abort", IrOBEX 1.2
+    // [opcode:1][length:2][Headers:var]
+    ParseHeaders(&aMessage->mData[3],
+                receivedLength - 3,
+                &pktHeaders);
+    ReplyToDisconnect();
+    AfterOppDisconnected();
+    if (opCode == ObexRequestCode::Abort) {
+      DeleteReceivedFile();
+    }
+    FileTransferComplete();
+  } else if (opCode == ObexRequestCode::Put ||
+             opCode == ObexRequestCode::PutFinal) {
+    int headerStartIndex = 0;
+
+    // The first part of each Put packet
+    if (mReceivedDataBufferOffset == 0) {
+      // Section 3.3.3 "Put", IrOBEX 1.2
+      // [opcode:1][length:2][Headers:var]
+      headerStartIndex = 3;
+      // The largest buffer size is 65535 because packetLength is a
+      // 2-byte value (0 ~ 0xFFFF).
+      mReceivedDataBuffer = new uint8_t[packetLength];
+      mPacketLeftLength = packetLength;
+
+      /*
+       * A PUT request from remote devices may be divided into multiple parts.
+       * In other words, one request may need to be received multiple times,
+       * so here we keep a variable mPacketLeftLength to indicate if current
+       * PUT request is done.
+       */
+      mPutFinalFlag = (opCode == ObexRequestCode::PutFinal);
+    }
+
+    memcpy(mReceivedDataBuffer.get() + mReceivedDataBufferOffset,
+           &aMessage->mData[headerStartIndex],
+           receivedLength - headerStartIndex);
+
+    mPacketLeftLength -= receivedLength;
+    mReceivedDataBufferOffset += receivedLength - headerStartIndex;
+    if (mPacketLeftLength) {
+      return;
+    }
+
+    // A Put packet is received completely
+    ParseHeaders(mReceivedDataBuffer.get(),
+                 mReceivedDataBufferOffset,
+                 &pktHeaders);
+    ExtractPacketHeaders(pktHeaders);
+
+    mReceivedDataBufferOffset = 0;
+
+    // When we cancel the transfer, delete the file and notify complemention
+    if (mAbortFlag) {
+      ReplyToPut(mPutFinalFlag, !mAbortFlag);
+      sSentFileLength += mBodySegmentLength;
+      DeleteReceivedFile();
+      FileTransferComplete();
+      return;
+    }
+
+    // Wait until get confirmation from user, then create file and write to it
+    if (mWaitingForConfirmationFlag) {
+      ReceivingFileConfirmation();
+      sSentFileLength += mBodySegmentLength;
+      return;
+    }
+
+    // Already get confirmation from user, create a new file if needed and
+    // write to output stream
+    if (mNewFileFlag) {
+      StartFileTransfer();
+      if (!CreateFile()) {
+        ReplyToPut(mPutFinalFlag, false);
+        return;
+      }
+      mNewFileFlag = false;
+    }
+
+    if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
+      ReplyToPut(mPutFinalFlag, false);
+      return;
+    }
+
+    ReplyToPut(mPutFinalFlag, true);
+
+    // Send progress update
+    sSentFileLength += mBodySegmentLength;
+    if (sSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
+      UpdateProgress();
+      mUpdateProgressCounter = sSentFileLength / kUpdateProgressBase + 1;
+    }
+
+    // Success to receive a file and notify completion
+    if (mPutFinalFlag) {
+      mSuccessFlag = true;
+      FileTransferComplete();
+    }
+  } else {
+    NS_WARNING("Unhandled ObexRequestCode");
+  }
+}
+
+void
+BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
+{
+  uint8_t opCode;
+  int packetLength;
+  int receivedLength = aMessage->mSize;
+
+  if (mPacketLeftLength > 0) {
+    opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
     packetLength = mPacketLeftLength;
   } else {
     opCode = aMessage->mData[0];
     packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
   }
 
-  if (mLastCommand == ObexRequestCode::Connect) {
-    if (opCode == ObexResponseCode::Success) {
-      AfterOppConnected();
-
-      // Keep remote information
-      mRemoteObexVersion = aMessage->mData[3];
-      mRemoteConnectionFlags = aMessage->mData[4];
-      mRemoteMaxPacketLength =
-        (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
-
-      MOZ_ASSERT(!sFileName.IsEmpty());
-      MOZ_ASSERT(mBlob);
-      /*
-       * Before sending content, we have to send a header including
-       * information such as file name, file length and content type.
-       */
-      nsresult rv = mBlob->GetType(sContentType);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Can't get content type");
-        SendDisconnectRequest();
-        return;
-      }
+  // Check response code
+  if (mLastCommand == ObexRequestCode::Put &&
+      opCode != ObexResponseCode::Continue) {
+    NS_WARNING("[OPP] Put(0x02) failed");
+    SendDisconnectRequest();
+    return;
+  } else if (mLastCommand == ObexRequestCode::Abort ||
+             mLastCommand == ObexRequestCode::Connect ||
+             mLastCommand == ObexRequestCode::Disconnect ||
+             mLastCommand == ObexRequestCode::PutFinal){
+    if (opCode != ObexResponseCode::Success) {
+      nsAutoCString str;
+      str += "[OPP] 0x";
+      str += mLastCommand;
+      str += " failed";
+      NS_WARNING(str.get());
+      return;
+    }
+  }
 
-      uint64_t fileLength;
-      rv = mBlob->GetSize(&fileLength);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Can't get file size");
-        SendDisconnectRequest();
-        return;
-      }
-
-      // Currently we keep the size of files which were sent/received via
-      // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
-      // is only 4-byte long. Although it is possible to transfer a file
-      // larger than UINT32_MAX, it needs to parse another OBEX Header
-      // and I would like to leave it as a feature.
-      if (fileLength > (uint64_t)UINT32_MAX) {
-        NS_WARNING("The file size is too large for now");
-        SendDisconnectRequest();
-        return;
-      }
-
-      sFileLength = fileLength;
-
-      rv = NS_NewThread(getter_AddRefs(mReadFileThread));
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Can't create thread");
-        SendDisconnectRequest();
-        return;
-      }
-
-      sInstance->SendPutHeaderRequest(sFileName, sFileLength);
-      StartFileTransfer();
-    } else {
-      SendDisconnectRequest();
-    }
+  if (mLastCommand == ObexRequestCode::PutFinal) {
+    mSuccessFlag = true;
+    FileTransferComplete();
+    SendDisconnectRequest();
+  } else if (mLastCommand == ObexRequestCode::Abort) {
+    SendDisconnectRequest();
+    FileTransferComplete();
   } else if (mLastCommand == ObexRequestCode::Disconnect) {
     AfterOppDisconnected();
     CloseSocket();
+  } else if (mLastCommand == ObexRequestCode::Connect) {
+    MOZ_ASSERT(!sFileName.IsEmpty());
+    MOZ_ASSERT(mBlob);
+
+    AfterOppConnected();
+
+    // Keep remote information
+    mRemoteObexVersion = aMessage->mData[3];
+    mRemoteConnectionFlags = aMessage->mData[4];
+    mRemoteMaxPacketLength =
+      (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
+
+    /*
+     * Before sending content, we have to send a header including
+     * information such as file name, file length and content type.
+     */
+    if (ExtractBlobHeaders()) {
+      sInstance->SendPutHeaderRequest(sFileName, sFileLength);
+      StartFileTransfer();
+    }
   } else if (mLastCommand == ObexRequestCode::Put) {
-    if (opCode != ObexResponseCode::Continue) {
-      NS_WARNING("[OPP] Put failed");
-      SendDisconnectRequest();
-      return;
-    }
-
     if (mAbortFlag) {
       SendAbortRequest();
       return;
     }
 
     if (kUpdateProgressBase * mUpdateProgressCounter < sSentFileLength) {
       UpdateProgress();
       mUpdateProgressCounter = sSentFileLength / kUpdateProgressBase + 1;
@@ -593,141 +802,31 @@ BluetoothOppManager::ReceiveSocketData(U
     }
 
     nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream);
     rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Cannot dispatch read file task!");
       SendDisconnectRequest();
     }
-  } else if (mLastCommand == ObexRequestCode::PutFinal) {
-    if (opCode != ObexResponseCode::Success) {
-      NS_WARNING("[OPP] PutFinal failed");
-      SendDisconnectRequest();
-      return;
-    }
-
-    mSuccessFlag = true;
-    SendDisconnectRequest();
-  } else if (mLastCommand == ObexRequestCode::Abort) {
-    if (opCode != ObexResponseCode::Success) {
-      NS_WARNING("[OPP] Abort failed");
-    }
-    SendDisconnectRequest();
   } else {
-    // Remote request or unknown mLastCommand
-    ObexHeaderSet pktHeaders(opCode);
-
-    if (opCode == ObexRequestCode::Connect) {
-      // Section 3.3.1 "Connect", IrOBEX 1.2
-      // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
-      // [Headers:var]
-      ParseHeaders(&aMessage->mData[7],
-                   receivedLength - 7,
-                   &pktHeaders);
-      ReplyToConnect();
-      AfterOppConnected();
-      mTransferMode = true;
-    } else if (opCode == ObexRequestCode::Disconnect) {
-      // Section 3.3.2 "Disconnect", IrOBEX 1.2
-      // [opcode:1][length:2][Headers:var]
-      ParseHeaders(&aMessage->mData[3],
-                  receivedLength - 3,
-                  &pktHeaders);
-      ReplyToDisconnect();
-      AfterOppDisconnected();
-    } else if (opCode == ObexRequestCode::Put ||
-               opCode == ObexRequestCode::PutFinal) {
-      int headerStartIndex = 0;
-
-      if (mReceivedDataBufferOffset == 0) {
-        // Section 3.3.3 "Put", IrOBEX 1.2
-        // [opcode:1][length:2][Headers:var]
-        headerStartIndex = 3;
-
-        // The largest buffer size is 65535 because packetLength is a
-        // 2-byte value (0 ~ 0xFFFF).
-        mReceivedDataBuffer = new uint8_t[packetLength];
-        mPacketLeftLength = packetLength;
-
-        /*
-         * A PUT request from remote devices may be divided into multiple parts.
-         * In other words, one request may need to be received multiple times,
-         * so here we keep a variable mPacketLeftLength to indicate if current
-         * PUT request is done.
-         */
-        mPutFinal = (opCode == ObexRequestCode::PutFinal);
-      }
-
-      memcpy(mReceivedDataBuffer.get() + mReceivedDataBufferOffset,
-             &aMessage->mData[headerStartIndex],
-             receivedLength - headerStartIndex);
+    NS_WARNING("Unhandled ObexRequestCode");
+  }
+}
 
-      mPacketLeftLength -= receivedLength;
-      mReceivedDataBufferOffset += receivedLength - headerStartIndex;
-
-      if (mPacketLeftLength == 0) {
-        ParseHeaders(mReceivedDataBuffer.get(),
-                     mReceivedDataBufferOffset,
-                     &pktHeaders);
-
-        if (pktHeaders.Has(ObexHeaderId::Name)) {
-          pktHeaders.GetName(sFileName);
-        }
-
-        if (pktHeaders.Has(ObexHeaderId::Type)) {
-          pktHeaders.GetContentType(sContentType);
-        }
-
-        if (pktHeaders.Has(ObexHeaderId::Length)) {
-          pktHeaders.GetLength(&sFileLength);
-        }
-
-        if (pktHeaders.Has(ObexHeaderId::Body) ||
-            pktHeaders.Has(ObexHeaderId::EndOfBody)) {
-          uint8_t* bodyPtr;
-          pktHeaders.GetBody(&bodyPtr);
-          mBodySegment = bodyPtr;
-
-          pktHeaders.GetBodyLength(&mBodySegmentLength);
+// Virtual function of class SocketConsumer
+void
+BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
+{
+  if (mLastCommand) {
+    ClientDataHandler(aMessage);
+    return;
+  }
 
-          if (!mWaitingForConfirmationFlag) {
-            if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
-              CloseSocket();
-            }
-          }
-        }
-
-        mReceivedDataBufferOffset = 0;
-
-        if (mWaitingForConfirmationFlag) {
-          // Note that we create file before sending system message here,
-          // so that the correct file name will be used.
-          if (!CreateFile()) {
-            ReplyToPut(mPutFinal, false);
-          } else {
-            ReceivingFileConfirmation();
-          }
-        } else {
-          ReplyToPut(mPutFinal, !mAbortFlag);
-
-          // Send progress notification
-          sSentFileLength += mBodySegmentLength;
-          if (sSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
-            UpdateProgress();
-            mUpdateProgressCounter = sSentFileLength / kUpdateProgressBase + 1;
-          }
-
-          if (mPutFinal) {
-            mSuccessFlag = true;
-          }
-        }
-      }
-    }
-  }
+  ServerDataHandler(aMessage);
 }
 
 void
 BluetoothOppManager::SendConnectRequest()
 {
   if (mConnected) return;
 
   // Section 3.3.1 "Connect", IrOBEX 1.2
@@ -922,16 +1021,20 @@ BluetoothOppManager::ReplyToPut(bool aFi
   UnixSocketRawData* s = new UnixSocketRawData(index);
   memcpy(s->mData, req, s->mSize);
   SendSocketData(s);
 }
 
 void
 BluetoothOppManager::FileTransferComplete()
 {
+  if (mSendTransferCompleteFlag) {
+    return;
+  }
+
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
   type.AssignLiteral("bluetooth-opp-transfer-complete");
 
   name.AssignLiteral("address");
   v = mConnectedDeviceAddress;
   parameters.AppendElement(BluetoothNamedValue(name, v));
@@ -955,16 +1058,18 @@ BluetoothOppManager::FileTransferComplet
   name.AssignLiteral("contentType");
   v = sContentType;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     NS_WARNING("Failed to broadcast [bluetooth-opp-transfer-complete]");
     return;
   }
+
+  mSendTransferCompleteFlag = true;
 }
 
 void
 BluetoothOppManager::StartFileTransfer()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
@@ -1092,25 +1197,31 @@ BluetoothOppManager::OnConnectError()
   CloseSocket();
   mSocketStatus = GetConnectionStatus();
   Listen();
 }
 
 void
 BluetoothOppManager::OnDisconnect()
 {
+  /**
+   * It is valid for a bluetooth device which is transfering file via OPP
+   * closing socket without sending OBEX disconnect request first. So we
+   * delete the broken file when we failed to receive a file from the remote,
+   * and notify the transfer has been completed (but failed). We also call
+   * AfterOppDisconnected here to ensure all variables will be cleaned.
+   */
   if (mSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
-    if (!mSuccessFlag && mTransferMode) {
-      DeleteReceivedFile();
+    if (!mSuccessFlag) {
+      if (mTransferMode) {
+        DeleteReceivedFile();
+      }
+      FileTransferComplete();
     }
-    FileTransferComplete();
     Listen();
   } else if (mSocketStatus == SocketConnectionStatus::SOCKET_CONNECTING) {
     NS_WARNING("BluetoothOppManager got unexpected socket status!");
   }
 
-  // It is valid for a bluetooth device which is transfering file via OPP
-  // closing socket without sending OBEX disconnect request first. So we
-  // call AfterOppDisconnected here to ensure all variables will be cleaned.
   AfterOppDisconnected();
   mConnectedDeviceAddress.AssignLiteral("00:00:00:00:00:00");
   mSuccessFlag = false;
 }
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -12,32 +12,35 @@
 #include "mozilla/ipc/UnixSocket.h"
 
 class nsIOutputStream;
 class nsIInputStream;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
+class ObexHeaderSet;
 
 class BluetoothOppManager : public mozilla::ipc::UnixSocketConsumer
 {
 public:
   /*
    * Channel of reserved services are fixed values, please check
    * function add_reserved_service_records() in
    * external/bluetooth/bluez/src/adapter.c for more information.
    */
   static const int DEFAULT_OPP_CHANNEL = 10;
   static const int MAX_PACKET_LENGTH = 0xFFFE;
 
   ~BluetoothOppManager();
   static BluetoothOppManager* Get();
   void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage)
     MOZ_OVERRIDE;
+  void ClientDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
+  void ServerDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
 
   /*
    * If a application wnats to send a file, first, it needs to
    * call Connect() to create a valid RFCOMM connection. After
    * that, call SendFile()/StopSendingFile() to control file-sharing
    * process. During the file transfering process, the application
    * will receive several system messages which contain the processed
    * percentage of file. At the end, the application will get another
@@ -56,52 +59,107 @@ public:
 
   void SendConnectRequest();
   void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
   void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength,
                       bool aFinal);
   void SendDisconnectRequest();
   void SendAbortRequest();
 
+  void ExtractPacketHeaders(const ObexHeaderSet& aHeader);
+  bool ExtractBlobHeaders();
   nsresult HandleShutdown();
 private:
   BluetoothOppManager();
   void StartFileTransfer();
   void FileTransferComplete();
   void UpdateProgress();
   void ReceivingFileConfirmation();
   bool CreateFile();
   bool WriteToFile(const uint8_t* aData, int aDataLength);
   void DeleteReceivedFile();
   void ReplyToConnect();
   void ReplyToDisconnect();
   void ReplyToPut(bool aFinal, bool aContinue);
   void AfterOppConnected();
+  void AfterFirstPut();
   void AfterOppDisconnected();
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
 
+  /**
+   * RFCOMM socket status.
+   */
+  enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
+
+  /**
+   * OBEX session status.
+   * Set when OBEX session is established.
+   */
   bool mConnected;
   int mConnectionId;
-  int mLastCommand;
+  nsString mConnectedDeviceAddress;
+
+  /**
+   * Remote information
+   */
   uint8_t mRemoteObexVersion;
   uint8_t mRemoteConnectionFlags;
   int mRemoteMaxPacketLength;
-  bool mAbortFlag;
+
+  /**
+   * For sending files, we decide our next action based on current command and
+   * previous one.
+   * For receiving files, we don't need previous command and it is set to 0
+   * as a default value.
+   */
+  int mLastCommand;
+
   int mPacketLeftLength;
   int mBodySegmentLength;
   int mReceivedDataBufferOffset;
-  nsString mConnectedDeviceAddress;
-  bool mPutFinal;
+  int mUpdateProgressCounter;
+
+  /**
+   * Set when StopSendingFile() is called.
+   */
+  bool mAbortFlag;
+
+  /**
+   * Set when receiving the first PUT packet of a new file
+   */
+  bool mNewFileFlag;
+
+  /**
+   * Set when receiving a PutFinal packet
+   */
+  bool mPutFinalFlag;
+
+  /**
+   * Set when FileTransferComplete() is called
+   */
+  bool mSendTransferCompleteFlag;
+
+  /**
+   * Set when a transfer is successfully completed.
+   */
+  bool mSuccessFlag;
+
+  /**
+   * True: Receive file (Server)
+   * False: Send file (Client)
+   */
+  bool mTransferMode;
+
+  /**
+   * Set when receiving the first PUT packet and wait for
+   * ConfirmReceivingFile() to be called.
+   */
   bool mWaitingForConfirmationFlag;
-  int mUpdateProgressCounter;
-  bool mSuccessFlag;
-  bool mTransferMode;  // send:0, receive:1
-  enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
 
   nsAutoPtr<uint8_t> mBodySegment;
   nsAutoPtr<uint8_t> mReceivedDataBuffer;
 
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
--- a/dom/bluetooth/ObexBase.h
+++ b/dom/bluetooth/ObexBase.h
@@ -135,17 +135,17 @@ public:
   {
   }
 
   void AddHeader(ObexHeader* aHeader)
   {
     mHeaders.AppendElement(aHeader);
   }
 
-  void GetName(nsString& aRetName)
+  void GetName(nsString& aRetName) const
   {
     aRetName.Truncate();
 
     int length = mHeaders.Length();
 
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == ObexHeaderId::Name) {
         /*
@@ -162,80 +162,80 @@ public:
           aRetName += c;
         }
 
         break;
       }
     }
   }
 
-  void GetContentType(nsString& aRetContentType)
+  void GetContentType(nsString& aRetContentType) const
   {
     aRetContentType.Truncate();
 
     int length = mHeaders.Length();
 
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == ObexHeaderId::Type) {
         uint8_t* ptr = mHeaders[i]->mData.get();
         aRetContentType.AssignASCII((const char*)ptr);
         break;
       }
     }
   }
 
   // @return file length, 0 means file length is unknown.
-  void GetLength(uint32_t* aRetLength)
+  void GetLength(uint32_t* aRetLength) const
   {
     int length = mHeaders.Length();
     *aRetLength = 0;
 
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == ObexHeaderId::Length) {
         uint8_t* ptr = mHeaders[i]->mData.get();
         *aRetLength = ((uint32_t)ptr[0] << 24) |
                       ((uint32_t)ptr[1] << 16) |
                       ((uint32_t)ptr[2] << 8) |
                       ((uint32_t)ptr[3]);
         return;
       }
     }
   }
 
-  void GetBodyLength(int* aRetBodyLength)
+  void GetBodyLength(int* aRetBodyLength) const
   {
     int length = mHeaders.Length();
     *aRetBodyLength = 0;
 
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == ObexHeaderId::Body ||
           mHeaders[i]->mId == ObexHeaderId::EndOfBody) {
         *aRetBodyLength = mHeaders[i]->mDataLength;
         return;
       }
     }
   }
 
-  void GetBody(uint8_t** aRetBody)
+  void GetBody(uint8_t** aRetBody) const
   {
     int length = mHeaders.Length();
     *aRetBody = nullptr;
 
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == ObexHeaderId::Body ||
           mHeaders[i]->mId == ObexHeaderId::EndOfBody) {
         uint8_t* ptr = mHeaders[i]->mData.get();
         *aRetBody = new uint8_t[mHeaders[i]->mDataLength];
         memcpy(*aRetBody, ptr, mHeaders[i]->mDataLength);
         return;
       }
     }
   }
 
-  bool Has(ObexHeaderId aId)
+  bool Has(ObexHeaderId aId) const
   {
     int length = mHeaders.Length();
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == aId) {
         return true;
       }
     }
 
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -53,16 +53,18 @@ function BrowserElementChild() {
   // (via iframe.setVisible).  ownerVisible corresponds to whether the docShell
   // whose window owns this element is visible.
   //
   // Our docShell is visible iff _forcedVisible and _ownerVisible are both
   // true.
   this._forcedVisible = true;
   this._ownerVisible = true;
 
+  this._nextPaintHandler = null;
+
   this._init();
 };
 
 BrowserElementChild.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
@@ -104,21 +106,20 @@ BrowserElementChild.prototype = {
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMLinkAdded',
                      this._iconChangedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
-    this._afterPaintHandlerClosure = this._afterPaintHandler.bind(this);
-    addEventListener('MozAfterPaint',
-                     this._afterPaintHandlerClosure,
-                     /* useCapture = */ true,
-                     /* wantsUntrusted = */ false);
+    // Registers a MozAfterPaint handler for the very first paint.
+    this._addMozAfterPaintHandler(function () {
+      sendAsyncMsg('firstpaint');
+    });
 
     var self = this;
     function addMsgListener(msg, handler) {
       addMessageListener('browser-element-api:' + msg, handler.bind(self));
     }
 
     addMsgListener("purge-history", this._recvPurgeHistory);
     addMsgListener("get-screenshot", this._recvGetScreenshot);
@@ -130,16 +131,18 @@ BrowserElementChild.prototype = {
     addMsgListener("go-back", this._recvGoBack);
     addMsgListener("go-forward", this._recvGoForward);
     addMsgListener("reload", this._recvReload);
     addMsgListener("stop", this._recvStop);
     addMsgListener("unblock-modal-prompt", this._recvStopWaiting);
     addMsgListener("fire-ctx-callback", this._recvFireCtxCallback);
     addMsgListener("owner-visibility-change", this._recvOwnerVisibilityChange);
     addMsgListener("exit-fullscreen", this._recvExitFullscreen.bind(this));
+    addMsgListener("activate-next-paint-listener", this._activateNextPaintListener.bind(this));
+    addMsgListener("deactivate-next-paint-listener", this._deactivateNextPaintListener.bind(this));
 
     let els = Cc["@mozilla.org/eventlistenerservice;1"]
                 .getService(Ci.nsIEventListenerService);
 
     // We are using the system group for those events so if something in the
     // content called .stopPropagation() this will still be called.
     els.addSystemEventListener(global, 'keydown',
                                this._keyEventHandler.bind(this),
@@ -356,26 +359,49 @@ BrowserElementChild.prototype = {
         sendAsyncMsg('iconchange', e.target.href);
       }
       else {
         debug("Not top level!");
       }
     }
   },
 
-  _afterPaintHandler: function(e) {
-    let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
-    debug("Got afterpaint event: " + uri.spec);
-    if (uri.spec != "about:blank") {
-      /* this._afterPaintHandlerClosure == arguments.callee, except we're in
-       * strict mode so we don't have arguments.callee. */
-      removeEventListener('MozAfterPaint', this._afterPaintHandlerClosure,
-                          /* useCapture */ true);
+  _addMozAfterPaintHandler: function(callback) {
+    function onMozAfterPaint() {
+      let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
+      debug("Got afterpaint event: " + uri.spec);
+      if (uri.spec != "about:blank") {
+        removeEventListener('MozAfterPaint', onMozAfterPaint,
+                            /* useCapture = */ true);
+        callback();
+      }
+    }
+
+    addEventListener('MozAfterPaint', onMozAfterPaint, /* useCapture = */ true);
+    return onMozAfterPaint;
+  },
 
-      sendAsyncMsg('firstpaint');
+  _removeMozAfterPaintHandler: function(listener) {
+    removeEventListener('MozAfterPaint', listener,
+                        /* useCapture = */ true);
+  },
+
+  _activateNextPaintListener: function(e) {
+    if (!this._nextPaintHandler) {
+      this._nextPaintHandler = this._addMozAfterPaintHandler(function () {
+        this._nextPaintHandler = null;
+        sendAsyncMsg('nextpaint');
+      }.bind(this));
+    }
+  },
+
+  _deactivateNextPaintListener: function(e) {
+    if (this._nextPaintHandler) {
+      this._removeMozAfterPaintHandler(this._nextPaintHandler);
+      this._nextPaintHandler = null;
     }
   },
 
   _closeHandler: function(e) {
     let win = e.target;
     if (win != content || e.defaultPrevented) {
       return;
     }
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -171,16 +171,17 @@ BrowserElementParentFactory.prototype = 
   },
 };
 
 function BrowserElementParent(frameLoader, hasRemoteFrame) {
   debug("Creating new BrowserElementParent object for " + frameLoader);
   this._domRequestCounter = 0;
   this._pendingDOMRequests = {};
   this._hasRemoteFrame = hasRemoteFrame;
+  this._nextPaintListeners = [];
 
   this._frameLoader = frameLoader;
   this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
   if (!this._frameElement) {
     debug("No frame element?");
     return;
   }
 
@@ -208,16 +209,17 @@ function BrowserElementParent(frameLoade
   addMessageListener("loadend", this._fireEventFromMsg);
   addMessageListener("titlechange", this._fireEventFromMsg);
   addMessageListener("iconchange", this._fireEventFromMsg);
   addMessageListener("close", this._fireEventFromMsg);
   addMessageListener("securitychange", this._fireEventFromMsg);
   addMessageListener("error", this._fireEventFromMsg);
   addMessageListener("scroll", this._fireEventFromMsg);
   addMessageListener("firstpaint", this._fireEventFromMsg);
+  addMessageListener("nextpaint", this._recvNextPaint);
   addMessageListener("keyevent", this._fireKeyEvent);
   addMessageListener("showmodalprompt", this._handleShowModalPrompt);
   addMessageListener('got-purge-history', this._gotDOMRequestResult);
   addMessageListener('got-screenshot', this._gotDOMRequestResult);
   addMessageListener('got-can-go-back', this._gotDOMRequestResult);
   addMessageListener('got-can-go-forward', this._gotDOMRequestResult);
   addMessageListener('fullscreen-origin-change', this._remoteFullscreenOriginChange);
   addMessageListener('rollback-fullscreen', this._remoteFrameFullscreenReverted);
@@ -252,16 +254,18 @@ function BrowserElementParent(frameLoade
     defineMethod('sendTouchEvent', this._sendTouchEvent);
   }
   defineMethod('goBack', this._goBack);
   defineMethod('goForward', this._goForward);
   defineMethod('reload', this._reload);
   defineMethod('stop', this._stop);
   defineMethod('purgeHistory', this._purgeHistory);
   defineMethod('getScreenshot', this._getScreenshot);
+  defineMethod('addNextPaintListener', this._addNextPaintListener);
+  defineMethod('removeNextPaintListener', this._removeNextPaintListener);
   defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
   defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
 
   // Listen to visibilitychange on the iframe's owner window, and forward it
   // down to the child.
   this._window.addEventListener('visibilitychange',
                                 this._ownerVisibilityChange.bind(this),
                                 /* useCapture = */ false,
@@ -589,16 +593,51 @@ BrowserElementParent.prototype = {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
 
     return this._sendDOMRequest('get-screenshot',
                                 {width: width, height: height});
   },
 
+  _recvNextPaint: function(data) {
+    let listeners = this._nextPaintListeners;
+    this._nextPaintListeners = [];
+    for (let listener of listeners) {
+      try {
+        listener();
+      } catch (e) {
+        // If a listener throws we'll continue.
+      }
+    }
+  },
+
+  _addNextPaintListener: function(listener) {
+    if (typeof listener != 'function')
+      throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
+
+    if (this._nextPaintListeners.push(listener) == 1)
+      this._sendAsyncMsg('activate-next-paint-listener');
+  },
+
+  _removeNextPaintListener: function(listener) {
+    if (typeof listener != 'function')
+      throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
+
+    for (let i = this._nextPaintListeners.length - 1; i >= 0; i--) {
+      if (this._nextPaintListeners[i] == listener) {
+        this._nextPaintListeners.splice(i, 1);
+        break;
+      }
+    }
+
+    if (this._nextPaintListeners.length == 0)
+      this._sendAsyncMsg('deactivate-next-paint-listener');
+  },
+
   _fireKeyEvent: function(data) {
     let evt = this._window.document.createEvent("KeyboardEvent");
     evt.initKeyEvent(data.json.type, true, true, this._window,
                      false, false, false, false, // modifiers
                      data.json.keyCode,
                      data.json.charCode);
 
     this._frameElement.dispatchEvent(evt);
--- a/dom/browser-element/mochitest/Makefile.in
+++ b/dom/browser-element/mochitest/Makefile.in
@@ -66,16 +66,20 @@ MOCHITEST_FILES = \
 		test_browserElement_inproc_XFrameOptionsSameOrigin.html \
 		file_browserElement_XFrameOptionsSameOrigin.html \
 		browserElement_XFrameOptionsAllowFrom.js \
 		test_browserElement_inproc_XFrameOptionsAllowFrom.html \
 		file_browserElement_XFrameOptionsAllowFrom.html \
 		file_browserElement_XFrameOptionsAllowFrom.sjs \
 		browserElement_FirstPaint.js \
 		test_browserElement_inproc_FirstPaint.html \
+		browserElement_NextPaint.js \
+		test_browserElement_inproc_NextPaint.html \
+		test_browserElement_oop_NextPaint.html \
+		file_browserElement_NextPaint.html \
 		browserElement_Alert.js \
 		test_browserElement_inproc_Alert.html \
 		browserElement_AlertInFrame.js \
 		test_browserElement_inproc_AlertInFrame.html \
 		file_browserElement_AlertInFrame.html \
 		file_browserElement_AlertInFrame_Inner.html \
 		browserElement_TargetTop.js \
 		test_browserElement_inproc_TargetTop.html \
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_NextPaint.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 808231 - Add mozbrowsernextpaint event.
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  browserElementTestHelpers.setEnabledPref(true);
+  browserElementTestHelpers.addPermission();
+
+  var iframe = document.createElement('iframe');
+  iframe.mozbrowser = true;
+  document.body.appendChild(iframe);
+
+  // Add a first listener that we'll remove shortly after.
+  iframe.addNextPaintListener(wrongListener);
+
+  var gotFirstNextPaintEvent = false;
+  iframe.addNextPaintListener(function () {
+    ok(!gotFirstNextPaintEvent, 'got the first nextpaint event');
+
+    // Make sure we're only called once.
+    gotFirstNextPaintEvent = true;
+
+    iframe.addNextPaintListener(function () {
+      info('got the second nextpaint event');
+      SimpleTest.finish();
+    });
+
+    // Force the iframe to repaint.
+    SimpleTest.executeSoon(function () iframe.src += '#next');
+  });
+
+  // Remove the first listener to make sure it's not called.
+  iframe.removeNextPaintListener(wrongListener);
+  iframe.src = 'file_browserElement_NextPaint.html';
+}
+
+function wrongListener() {
+  ok(false, 'first listener should have been removed');
+}
+
+runTest();
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_NextPaint.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<script>
+addEventListener("hashchange", function () {
+  document.body.style.backgroundColor = "red";
+});
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_NextPaint.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 808231</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_NextPaint.js">
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_NextPaint.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 808231</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_NextPaint.js">
+</script>
+</body>
+</html>
\ No newline at end of file
--- a/dom/interfaces/html/Makefile.in
+++ b/dom/interfaces/html/Makefile.in
@@ -57,18 +57,16 @@ SDK_XPIDLSRCS =					\
 	nsIDOMHTMLObjectElement.idl		\
 	nsIDOMHTMLOptGroupElement.idl		\
 	nsIDOMHTMLOptionElement.idl		\
 	nsIDOMHTMLOptionsCollection.idl		\
 	nsIDOMHTMLOutputElement.idl		\
 	nsIDOMHTMLParagraphElement.idl		\
 	nsIDOMHTMLParamElement.idl		\
 	nsIDOMHTMLPreElement.idl		\
-	nsIDOMHTMLPropertiesCollection.idl  \
-	nsIDOMPropertyNodeList.idl  \
 	nsIDOMHTMLProgressElement.idl		\
 	nsIDOMHTMLQuoteElement.idl		\
 	nsIDOMHTMLScriptElement.idl		\
 	nsIDOMHTMLSelectElement.idl		\
 	nsIDOMHTMLStyleElement.idl		\
 	nsIDOMHTMLTableCaptionElem.idl		\
 	nsIDOMHTMLTableCellElement.idl		\
 	nsIDOMHTMLTableColElement.idl		\
--- a/dom/interfaces/html/nsIDOMHTMLElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLElement.idl
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMElement.idl"
 #include "nsIVariant.idl"
 
 interface nsIDOMHTMLMenuElement;
-interface nsIDOMHTMLPropertiesCollection;
 
 /**
  * The nsIDOMHTMLElement interface is the primary [X]HTML element
  * interface. It represents a single [X]HTML element in the document
  * tree.
  *
  * This interface is trying to follow the DOM Level 2 HTML specification:
  * http://www.w3.org/TR/DOM-Level-2-HTML/
@@ -29,17 +28,17 @@ interface nsIDOMHTMLElement : nsIDOMElem
            attribute DOMString        lang;
            attribute DOMString        dir;
            attribute DOMString        className;
   readonly attribute nsISupports      dataset;
 
            attribute boolean                        itemScope;
            attribute nsIVariant                     itemType;
            attribute DOMString                      itemId;
-  readonly attribute nsIDOMHTMLPropertiesCollection properties; 
+  readonly attribute nsISupports                    properties;
   // The following attributes are really nsDOMSettableTokenList, which has
   // PutForwards, so we express them as nsIVariants to deal with this.
            attribute nsIVariant                     itemValue;
            attribute nsIVariant                     itemProp;
            attribute nsIVariant                     itemRef;
 
   // user interaction
   /**
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLPropertiesCollection.idl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
-/* 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 "nsIDOMPropertyNodeList.idl"
-#include "nsIDOMDOMStringList.idl"
-
-// This interface should extend nsIDOMHTMLCollection, which will be fixed when
-// it is converted to webidl.
-[scriptable, uuid(b3a368e4-61a4-4578-94ce-57f98b0e79e8)]
-interface nsIDOMHTMLPropertiesCollection : nsISupports
-{
-  readonly attribute unsigned long    length;
-  readonly attribute nsIDOMDOMStringList names;
-
-  nsIDOMNode                item(in unsigned long index);
-  nsIDOMPropertyNodeList namedItem(in DOMString name);
-};
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMPropertyNodeList.idl
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
-/* 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 "nsIVariant.idl"
-
-interface nsIDOMNode;
-
-[scriptable, uuid(22c9c591-182d-4504-8a95-d3274a8b147a)]
-interface nsIDOMPropertyNodeList : nsISupports {
-  nsIDOMNode    item(in unsigned long index);
-  readonly attribute unsigned long          length;
-  nsIVariant getValues();
-};
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -5,25 +5,26 @@
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+# Bug 814718 and bug 818466 prevent us from running the following tests:
+#  test_getUserMedia_basicVideo.html
+#  test_getUserMedia_basicAudio.html
+#  test_getUserMedia_basicVideoAudio.html
+
 MOCHITEST_FILES = \
   test_getUserMedia_exceptions.html \
   head.js \
+  mediaStreamPlayback.js \
   $(NULL)
 
 # The following tests are leaking and cannot be run by default yet
 ifdef MOZ_WEBRTC_LEAKING_TESTS
 MOCHITEST_FILES += \
-# Bug 814718 and bug 818466 prevent us from running the following tests:
-#  test_getUserMedia_basicVideo.html \
-#  test_getUserMedia_basicAudio.html \
-#  test_getUserMedia_basicVideoAudio.html \
-#  mediaStreamPlayback.js \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/phonenumberutils/PhoneNumber.jsm
+++ b/dom/phonenumberutils/PhoneNumber.jsm
@@ -205,17 +205,17 @@ this.PhoneNumber = (function (dataBase) 
       var value = FormatNumber(this.regionMetaData, this.nationalNumber, false);
       Object.defineProperty(this, "nationalFormat", { value: value, enumerable: true });
       return value;
     },
     // +19497262896
     get internationalNumber() {
       var value = this.internationalFormat ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "")
                                            : null;
-      Object.defineProperty(this, "nationalNumber", { value: value, enumerable: true });
+      Object.defineProperty(this, "internationalNumber", { value: value, enumerable: true });
       return value;
     }
   };
 
   // Normalize a number by converting unicode numbers and symbols to their
   // ASCII equivalents and removing all non-dialable characters.
   function NormalizeNumber(number) {
     number = number.replace(UNICODE_DIGITS,
--- a/dom/sms/src/ril/SmsDatabaseService.js
+++ b/dom/sms/src/ril/SmsDatabaseService.js
@@ -358,83 +358,17 @@ SmsDatabaseService.prototype = {
           unreadCount: message.read ? 0 : 1
         }
       }
       cursor.continue();
     }
   },
 
   upgradeSchema5: function upgradeSchema5(transaction) {
-    let smsStore = transaction.objectStore(STORE_NAME);
-    let mostRecentStore = transaction.objectStore(MOST_RECENT_STORE_NAME);
-
-    smsStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        return;
-      }
-
-      let needsUpdate = false;
-      let message = cursor.value;
-      if (message.receiver) {
-        if (message.receiver !== "undefined") {
-          if (DEBUG) debug("upgrade message.receiver from: " + message.receiver + "\n");
-          let parsedNumber = PhoneNumberUtils.parse(message.receiver.toString());
-          if (parsedNumber && parsedNumber.internationalNumber) {
-            message.receiver = parsedNumber.internationalNumber;
-            needsUpdate = true;
-          }
-
-          if (DEBUG) debug("upgrade message.receiver to: " + message.receiver + "\n");
-        } else {
-          message.receiver = null;
-          needsUpdate = true;
-        }
-      }
-
-      if (message.sender) {
-        if (message.sender !== "undefined") {
-          if (DEBUG) debug("upgrade message.sender from: " + message.sender + "\n");
-          let parsedNumber = PhoneNumberUtils.parse(message.sender.toString());
-          if (parsedNumber && parsedNumber.internationalNumber) {
-            message.sender = parsedNumber.internationalNumber;
-            needsUpdate = true;
-            if (DEBUG) debug("upgrade message.sender to: " + message.sender + "\n");
-          }
-        } else {
-          message.sender = null;
-          needsUpdate = true;
-        }
-      }
-
-      if (needsUpdate) {
-        cursor.update(message);
-      }
-      cursor.continue();
-    }
-
-    mostRecentStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        return;
-      }
-
-      let entry = cursor.value;
-      if (entry.senderOrReceiver) {
-        if (DEBUG) debug("upgrade mostRecentStore from: " + entry.senderOrReceiver + "\n");
-        let parsedNumber = PhoneNumberUtils.parse(entry.senderOrReceiver);
-        if (parsedNumber && parsedNumber.internationalNumber) {
-          entry.senderOrReceiver = parsedNumber.internationalNumber;
-          cursor.update(entry);
-          if (DEBUG) debug("upgrade mostRecentStore to: " + entry.senderOrReceiver + "\n");
-        }
-      }
-
-      cursor.continue();
-    }
+    // Don't perform any upgrade. See Bug 819560.
   },
 
   /**
    * Helper function to make the intersection of the partial result arrays
    * obtained within createMessageList.
    *
    * @param keys
    *        Object containing the partial result arrays.
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -780,20 +780,23 @@ nsJSChannel::EvaluateScript()
     // EvaluateScript() succeeded, and we were not canceled, that
     // means there's data to parse as a result of evaluating the
     // script.
 
     // Get the stream channels load flags (!= mLoadFlags).
     nsLoadFlags loadFlags;
     mStreamChannel->GetLoadFlags(&loadFlags);
 
-    if (loadFlags & LOAD_DOCUMENT_URI) {
-        // We're loaded as the document channel. If we go on,
-        // we'll blow away the current document. Make sure that's
-        // ok. If so, stop all pending network loads.
+    uint32_t disposition;
+    if (NS_FAILED(mStreamChannel->GetContentDisposition(&disposition)))
+        disposition = nsIChannel::DISPOSITION_INLINE;
+    if (loadFlags & LOAD_DOCUMENT_URI && disposition != nsIChannel::DISPOSITION_ATTACHMENT) {
+        // We're loaded as the document channel and not expecting to download
+        // the result. If we go on, we'll blow away the current document. Make
+        // sure that's ok. If so, stop all pending network loads.
 
         nsCOMPtr<nsIDocShell> docShell;
         NS_QueryNotificationCallbacks(mStreamChannel, docShell);
         if (docShell) {
             nsCOMPtr<nsIContentViewer> cv;
             docShell->GetContentViewer(getter_AddRefs(cv));
 
             if (cv) {
--- a/dom/tests/browser/browser_webapps_permissions.js
+++ b/dom/tests/browser/browser_webapps_permissions.js
@@ -24,17 +24,17 @@ const TEST_MANIFEST_URL =
   "http://mochi.test:8888/browser/dom/tests/browser/test-webapp.webapp";
 const TEST_ORIGIN_URL = "http://mochi.test:8888";
 
 const installedPermsToTest = {
   "geolocation": "prompt",
   "alarms": "allow",
   "contacts-write": "prompt",
   "contacts-read": "prompt",
-  "device-storage:apps-read": "prompt",
+  "device-storage:apps-read": "deny",
   "device-storage:apps-write": "unknown"
 };
 
 const uninstalledPermsToTest = {
   "geolocation": "unknown",
   "alarms": "unknown",
   "contacts-read": "unknown",
   "device-storage:apps-read": "unknown",
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1000,16 +1000,31 @@ var WifiManager = (function() {
   // Initial state
   manager.state = "UNINITIALIZED";
   manager.enabled = false;
   manager.supplicantStarted = false;
   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   manager.authenticationFailuresCount = 0;
   manager.loopDetectionCount = 0;
 
+  const DRIVER_READY_WAIT = 2000;
+  var waitForDriverReadyTimer = null;
+  function cancelWaitForDriverReadyTimer() {
+    if (waitForDriverReadyTimer) {
+      waitForDriverReadyTimer.cancel();
+      waitForDriverReadyTimer = null;
+    }
+  };
+  function createWaitForDriverReadyTimer(onTimeout) {
+    waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    waitForDriverReadyTimer.initWithCallback(onTimeout,
+                                             DRIVER_READY_WAIT,
+                                             Ci.nsITimer.TYPE_ONE_SHOT);
+  };
+
   // Public interface of the wifi service
   manager.setWifiEnabled = function(enable, callback) {
     if (enable === manager.enabled) {
       callback("no change");
       return;
     }
 
     if (enable) {
@@ -1040,19 +1055,18 @@ var WifiManager = (function() {
 
         prepareForStartup(function() {
           loadDriver(function (status) {
             if (status < 0) {
               callback(status);
               return;
             }
 
-            let timer;
             function doStartSupplicant() {
-              timer = null;
+              cancelWaitForDriverReadyTimer();
               startSupplicant(function (status) {
                 if (status < 0) {
                   unloadDriver(function() {
                     callback(status);
                   });
                   return;
                 }
 
@@ -1061,19 +1075,18 @@ var WifiManager = (function() {
                   callback(ok ? 0 : -1);
                 });
               });
             }
 
             // Driver startup on certain platforms takes longer than it takes for us
             // to return from loadDriver, so wait 2 seconds before starting
             // the supplicant to give it a chance to start.
-            timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-            timer.init(doStartSupplicant, 2000, Ci.nsITimer.TYPE_ONE_SHOT);
-          });
+            createWaitForDriverReadyTimer(doStartSupplicant);
+         });
         });
       });
     } else {
       // Note these following calls ignore errors. If we fail to kill the
       // supplicant gracefully, then we need to continue telling it to die
       // until it does.
       terminateSupplicant(function (ok) {
         manager.connectionDropped(function () {
@@ -1099,25 +1112,33 @@ var WifiManager = (function() {
           return;
         }
         manager.ifname = ifname;
         loadDriver(function (status) {
           if (status < 0) {
             callback(enabled);
             return;
           }
-          WifiNetworkInterface.name = manager.ifname;
-          manager.state = "WIFITETHERING";
-          // Turning on wifi tethering.
-          gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, function(result) {
-            // Pop out current request.
-            callback(enabled);
-            // Should we fire a dom event if we fail to set wifi tethering  ?
-            debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
-          });
+
+          function doStartWifiTethering() {
+            cancelWaitForDriverReadyTimer();
+            WifiNetworkInterface.name = manager.ifname;
+            manager.state = "WIFITETHERING";
+            gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, function(result) {
+              // Pop out current request.
+              callback(enabled);
+              // Should we fire a dom event if we fail to set wifi tethering  ?
+              debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
+            });
+          }
+
+          // Driver startup on certain platforms takes longer than it takes
+          // for us to return from loadDriver, so wait 2 seconds before
+          // turning on Wifi tethering.
+          createWaitForDriverReadyTimer(doStartWifiTethering);
         });
       });
     } else {
       manager.state = "UNINITIALIZED";
       gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, function(result) {
         // Should we fire a dom event if we fail to set wifi tethering  ?
         debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
         // Unload wifi driver even if we fail to control wifi tethering.
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -994,75 +994,79 @@ public:
     }
 
     JSString* filename = JS_NewUCStringCopyN(aCx, aFilename.get(),
                                              aFilename.Length());
     if (!filename) {
       return false;
     }
 
-    // First fire an ErrorEvent at the worker.
-    if (aTarget) {
-      JSObject* event = 
-        CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate);
-      if (!event) {
-        return false;
-      }
-
-      bool preventDefaultCalled;
-      if (!DispatchEventToTarget(aCx, aTarget, event, &preventDefaultCalled)) {
-        return false;
-      }
-
-      if (preventDefaultCalled) {
-        return true;
-      }
-    }
-
-    // Now fire an event at the global object, but don't do that if the error
-    // code is too much recursion and this is the same script threw the error.
-    if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
-      aTarget = JS_GetGlobalForScopeChain(aCx);
-      NS_ASSERTION(aTarget, "This should never be null!");
-
-      bool preventDefaultCalled;
-      nsIScriptGlobalObject* sgo;
-
-      if (aWorkerPrivate ||
-          !(sgo = nsJSUtils::GetStaticScriptGlobal(aTarget))) {
-        // Fire a normal ErrorEvent if we're running on a worker thread.
+    // We should not fire error events for warnings but instead make sure that
+    // they show up in the error console.
+    if (!JSREPORT_IS_WARNING(aFlags)) {
+      // First fire an ErrorEvent at the worker.
+      if (aTarget) {
         JSObject* event =
-          CreateErrorEvent(aCx, message, filename, aLineNumber, false);
+          CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate);
         if (!event) {
           return false;
         }
 
-        if (!DispatchEventToTarget(aCx, aTarget, event,
-                                   &preventDefaultCalled)) {
+        bool preventDefaultCalled;
+        if (!DispatchEventToTarget(aCx, aTarget, event, &preventDefaultCalled)) {
           return false;
         }
+
+        if (preventDefaultCalled) {
+          return true;
+        }
       }
-      else {
-        // Icky, we have to fire an nsScriptErrorEvent...
-        nsScriptErrorEvent event(true, NS_LOAD_ERROR);
-        event.lineNr = aLineNumber;
-        event.errorMsg = aMessage.get();
-        event.fileName = aFilename.get();
-
-        nsEventStatus status = nsEventStatus_eIgnore;
-        if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
-          NS_WARNING("Failed to dispatch main thread error event!");
-          status = nsEventStatus_eIgnore;
+
+      // Now fire an event at the global object, but don't do that if the error
+      // code is too much recursion and this is the same script threw the error.
+      if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
+        aTarget = JS_GetGlobalForScopeChain(aCx);
+        NS_ASSERTION(aTarget, "This should never be null!");
+
+        bool preventDefaultCalled;
+        nsIScriptGlobalObject* sgo;
+
+        if (aWorkerPrivate ||
+            !(sgo = nsJSUtils::GetStaticScriptGlobal(aTarget))) {
+          // Fire a normal ErrorEvent if we're running on a worker thread.
+          JSObject* event =
+            CreateErrorEvent(aCx, message, filename, aLineNumber, false);
+          if (!event) {
+            return false;
+          }
+
+          if (!DispatchEventToTarget(aCx, aTarget, event,
+                                     &preventDefaultCalled)) {
+            return false;
+          }
         }
-
-        preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
-      }
-
-      if (preventDefaultCalled) {
-        return true;
+        else {
+          // Icky, we have to fire an nsScriptErrorEvent...
+          nsScriptErrorEvent event(true, NS_LOAD_ERROR);
+          event.lineNr = aLineNumber;
+          event.errorMsg = aMessage.get();
+          event.fileName = aFilename.get();
+
+          nsEventStatus status = nsEventStatus_eIgnore;
+          if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
+            NS_WARNING("Failed to dispatch main thread error event!");
+            status = nsEventStatus_eIgnore;
+          }
+
+          preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
+        }
+
+        if (preventDefaultCalled) {
+          return true;
+        }
       }
     }
 
     // Now fire a runnable to do the same on the parent's thread if we can.
     if (aWorkerPrivate) {
       nsRefPtr<ReportErrorRunnable> runnable =
         new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
                                 aLineNumber, aColumnNumber, aFlags,
@@ -1480,17 +1484,17 @@ struct WorkerJSRuntimeStats : public JS:
     }
   }
 
   virtual void initExtraCompartmentStats(JSCompartment *c,
                                          JS::CompartmentStats *cstats) MOZ_OVERRIDE
   {
     MOZ_ASSERT(!cstats->extra1);
     MOZ_ASSERT(!cstats->extra2);
-    
+
     // ReportJSRuntimeExplicitTreeStats expects that cstats->{extra1,extra2}
     // are char pointers.
 
     // This is the |cJSPathPrefix|.  Each worker has exactly two compartments:
     // one for atoms, and one for everything else.
     nsCString cJSPathPrefix(mRtPath);
     cJSPathPrefix += js::IsAtomsCompartment(c)
                    ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
@@ -1500,17 +1504,17 @@ struct WorkerJSRuntimeStats : public JS:
     // This is the |cDOMPathPrefix|, which should never be used when reporting
     // with workers (hence the "?!").
     cstats->extra2 = (void *)"explicit/workers/?!/";
   }
 
 private:
   nsCString mRtPath;
 };
-  
+
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
 {