Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 20 Oct 2014 18:56:56 -0700
changeset 211371 29fbfc1b31aa
parent 211370 290f799b4f58 (current diff)
parent 211292 223e2f4b0d47 (diff)
child 211372 0808729b24e8
child 211472 95f1fe635f8b
child 211478 f1f0790e35bb
push id50691
push userkwierso@gmail.com
push date2014-10-21 02:08 +0000
treeherdermozilla-inbound@0808729b24e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
29fbfc1b31aa / 36.0a1 / 20141021030208 / files
nightly linux64
29fbfc1b31aa / 36.0a1 / 20141021030208 / files
nightly mac
29fbfc1b31aa / 36.0a1 / 20141021030208 / files
nightly win32
29fbfc1b31aa / 36.0a1 / 20141021030208 / files
nightly win64
29fbfc1b31aa / 36.0a1 / 20141021030208 / 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 inbound to m-c a=merge
dom/plugins/ipc/PPluginIdentifier.ipdl
dom/plugins/ipc/PluginIdentifierChild.cpp
dom/plugins/ipc/PluginIdentifierChild.h
dom/plugins/ipc/PluginIdentifierParent.cpp
dom/plugins/ipc/PluginIdentifierParent.h
js/xpconnect/tests/chrome/test_bug760109.xul
testing/web-platform/meta/resource-timing/test_resource_timing.html.ini
testing/web-platform/meta/websockets/interfaces/WebSocket/constants/002.html.ini
testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/setTimeout.html.ini
testing/web-platform/tests/conformance-checkers/html/attributes/accesskey/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/accesskey/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/accesskey/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/accesskey/004-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/data/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/data/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/001-isvalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/003-isvalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/004-haswarn.html
testing/web-platform/tests/conformance-checkers/html/attributes/lang/005-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/role/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/spellcheck/050-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/spellcheck/053-isvalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/title/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/attributes/title/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/a/004-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/a/075-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/a/100-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/abbr/015-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/abbr/015-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/address/029-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/address/029-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/area/005-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/area/049-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/area/049-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/audio/074-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/audio/074-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/base/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/bdo/022-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/bdo/022-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/bdo/023-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/blockquote/028-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/blockquote/028-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/br/023-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/canvas/070-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/canvas/070-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/cite/021-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/cite/021-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/code/010-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/code/010-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/del/041-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/del/041-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dfn/016-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dfn/016-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dialog/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/div/033-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/div/033-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dl/032-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/dl/032-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/em/006-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/em/006-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/embed/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/embed/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/embed/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/footer/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/footer/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h1/034-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h1/034-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h2/035-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h2/035-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h3/036-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h3/036-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h4/037-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h4/037-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h5/038-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h5/038-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h5/039-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/h6/039-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/header/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/header/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/hr/026-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/hr/026-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/i/017-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/i/017-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/iframe/042-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/iframe/042-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/img/043-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/img/043-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/img/050-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/img/051-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/input/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/input/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/input/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ins/040-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ins/040-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/kbd/012-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/kbd/012-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/054-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/054-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/055-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/056-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/056-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/057-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/058-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/058-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/059-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/060-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/keygen/061-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/label/001-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/label/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/label/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/link/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/map/048-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/map/048-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/mark/009-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/noscript/005-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/object/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/object/044-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/object/044-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ol/031-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ol/031-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/option/080-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/option/080-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/option/081-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/option/081-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/p/025-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/p/025-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/param/046-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/param/046-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/pre/027-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/pre/027-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/q/013-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/q/013-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ruby/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ruby/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/s/011-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/samp/011-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/samp/011-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/script/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/small/008-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/small/008-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/span/014-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/span/014-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/strong/007-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/strong/007-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/015-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/016-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/016-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/017-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/017-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/018-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/style/104-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/sub/020-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/sub/020-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/sup/019-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/sup/019-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/table/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/table/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/table/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/time/001-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/u/001-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ul/030-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/ul/030-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/usemap/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/var/018-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/var/018-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/video/073-isvalid.html
testing/web-platform/tests/conformance-checkers/html/elements/video/073-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/004-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/005-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/006-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/007-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/008-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/009-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/010-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/012-novalid.html
testing/web-platform/tests/conformance-checkers/html/obsolete/013-novalid.html
testing/web-platform/tests/conformance-checkers/html/other/044-novalid.html
testing/web-platform/tests/conformance-checkers/html/other/045-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/001-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/002-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/003-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/004-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/005-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/006-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/007-novalid.html
testing/web-platform/tests/conformance-checkers/html/parser/008-novalid.html
testing/web-platform/tests/websockets/interfaces/WebSocket/protocol/001.html
--- a/accessible/jsat/Gestures.jsm
+++ b/accessible/jsat/Gestures.jsm
@@ -2,25 +2,25 @@
  * 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/. */
 
 /* global Components, GestureSettings, XPCOMUtils, Utils, Promise, Logger */
 /* exported GestureSettings, GestureTracker */
 
 /******************************************************************************
   All gestures have the following pathways when being resolved(v)/rejected(x):
-               Tap -> DoubleTap        (v)
+               Tap -> DoubleTap        (x)
                    -> Dwell            (x)
                    -> Swipe            (x)
 
-        AndroidTap -> TripleTap        (v)
+        AndroidTap -> TripleTap        (x)
                    -> TapHold          (x)
                    -> Swipe            (x)
 
-         DoubleTap -> TripleTap        (v)
+         DoubleTap -> TripleTap        (x)
                    -> TapHold          (x)
                    -> Explore          (x)
 
          TripleTap -> DoubleTapHold    (x)
                    -> Explore          (x)
 
              Dwell -> DwellEnd         (v)
 
@@ -355,29 +355,32 @@ Gesture.prototype = {
     // reject this gesture promise.
     return GestureSettings.maxConsecutiveGestureDelay;
   },
 
   /**
    * Clear the existing timer.
    */
   clearTimer: function Gesture_clearTimer() {
+    Logger.gesture('clearTimeout', this.type);
     clearTimeout(this._timer);
     delete this._timer;
   },
 
   /**
    * Start the timer for gesture timeout.
    * @param {Number} aTimeStamp An original pointer event's timeStamp that
    * started the gesture resolution sequence.
    */
   startTimer: function Gesture_startTimer(aTimeStamp) {
+    Logger.gesture('startTimer', this.type);
     this.clearTimer();
     let delay = this._getDelay(aTimeStamp);
     let handler = () => {
+      Logger.gesture('timer handler');
       delete this._timer;
       if (!this._inProgress) {
         this._deferred.reject();
       } else if (this._rejectToOnWait) {
         this._deferred.reject(this._rejectToOnWait);
       }
     };
     if (delay <= 0) {
@@ -667,69 +670,99 @@ DoubleTapHoldEnd.prototype = Object.crea
 DoubleTapHoldEnd.prototype.type = 'doubletapholdend';
 
 /**
  * A common tap gesture object.
  * @param {Number} aTimeStamp An original pointer event's timeStamp that started
  * the gesture resolution sequence.
  * @param {Object} aPoints An existing set of points (from previous events).
  * @param {?String} aLastEvent Last pointer event type.
- * @param {Function} aRejectTo A constructor for the next gesture to reject to
- * in case no pointermove or pointerup happens within the
+ * @param {Function} aRejectToOnWait A constructor for the next gesture to
+ * reject to in case no pointermove or pointerup happens within the
  * GestureSettings.dwellThreshold.
+ * @param {Function} aRejectToOnPointerDown A constructor for the gesture to
+ * reject to if a finger comes down immediately after the tap.
  * @param {Function} aTravelTo An optional constuctor for the next gesture to
  * reject to in case the the TravelGesture test fails.
  */
-function TapGesture(aTimeStamp, aPoints, aLastEvent, aRejectTo, aTravelTo) {
-  this._rejectToOnWait = aRejectTo;
+function TapGesture(aTimeStamp, aPoints, aLastEvent, aRejectToOnWait, aTravelTo, aRejectToOnPointerDown) {
+  this._rejectToOnWait = aRejectToOnWait;
+  this._rejectToOnPointerDown = aRejectToOnPointerDown;
   // If the pointer travels, reject to aTravelTo.
   TravelGesture.call(this, aTimeStamp, aPoints, aLastEvent, aTravelTo,
     TAP_MAX_RADIUS);
 }
 
 TapGesture.prototype = Object.create(TravelGesture.prototype);
 TapGesture.prototype._getDelay = function TapGesture__getDelay() {
   // If, for TapGesture, no pointermove or pointerup happens within the
   // GestureSettings.dwellThreshold, reject.
   // Note: the original pointer event's timeStamp is irrelevant here.
   return GestureSettings.dwellThreshold;
 };
 
+TapGesture.prototype.pointerup = function TapGesture_pointerup(aPoints) {
+    if (this._rejectToOnPointerDown) {
+      let complete = this._update(aPoints, 'pointerup', false, true);
+      if (complete) {
+        this.clearTimer();
+        if (GestureSettings.maxConsecutiveGestureDelay) {
+          this._pointerUpTimer = setTimeout(() => {
+            delete this._pointerUpTimer;
+            this._deferred.resolve();
+          }, GestureSettings.maxConsecutiveGestureDelay);
+        } else {
+          this._deferred.resolve();
+        }
+      }
+    } else {
+      TravelGesture.prototype.pointerup.call(this, aPoints);
+    }
+};
+
+TapGesture.prototype.pointerdown = function TapGesture_pointerdown(aPoints, aTimeStamp) {
+  TravelGesture.prototype.pointerdown.call(this, aPoints, aTimeStamp);
+  if (this._pointerUpTimer) {
+    clearTimeout(this._pointerUpTimer);
+    delete this._pointerUpTimer;
+    this._deferred.reject(this._rejectToOnPointerDown);
+  }
+};
+
+
 /**
  * Tap gesture.
  * @param {Number} aTimeStamp An original pointer event's timeStamp that started
  * the gesture resolution sequence.
  * @param {Object} aPoints An existing set of points (from previous events).
  * @param {?String} aLastEvent Last pointer event type.
  */
 function Tap(aTimeStamp, aPoints, aLastEvent) {
   // If the pointer travels, reject to Swipe.
-  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, Dwell, Swipe);
+  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, Dwell, Swipe, DoubleTap);
 }
 
 Tap.prototype = Object.create(TapGesture.prototype);
 Tap.prototype.type = 'tap';
-Tap.prototype.resolveTo = DoubleTap;
 
 /**
  * Tap (multi) gesture on Android.
  * @param {Number} aTimeStamp An original pointer event's timeStamp that started
  * the gesture resolution sequence.
  * @param {Object} aPoints An existing set of points (from previous events).
  * @param {?String} aLastEvent Last pointer event type.
  */
 function AndroidTap(aTimeStamp, aPoints, aLastEvent) {
   // If the pointer travels, reject to Swipe. On dwell threshold reject to
   // TapHold.
-  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, Swipe);
+  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, Swipe, TripleTap);
 }
 AndroidTap.prototype = Object.create(TapGesture.prototype);
 // Android double taps are translated to single taps.
 AndroidTap.prototype.type = 'doubletap';
-AndroidTap.prototype.resolveTo = TripleTap;
 
 /**
  * Clear the pointerup handler timer in case of the 3 pointer swipe.
  */
 AndroidTap.prototype.clearThreeFingerSwipeTimer = function AndroidTap_clearThreeFingerSwipeTimer() {
   clearTimeout(this._threeFingerSwipeTimer);
   delete this._threeFingerSwipeTimer;
 };
@@ -762,31 +795,32 @@ AndroidTap.prototype.pointerup = functio
 /**
  * Double Tap gesture.
  * @param {Number} aTimeStamp An original pointer event's timeStamp that started
  * the gesture resolution sequence.
  * @param {Object} aPoints An existing set of points (from previous events).
  * @param {?String} aLastEvent Last pointer event type.
  */
 function DoubleTap(aTimeStamp, aPoints, aLastEvent) {
-  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold);
+  this._inProgress = true;
+  TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, null, TripleTap);
 }
 
 DoubleTap.prototype = Object.create(TapGesture.prototype);
 DoubleTap.prototype.type = 'doubletap';
-DoubleTap.prototype.resolveTo = TripleTap;
 
 /**
  * Triple Tap gesture.
  * @param {Number} aTimeStamp An original pointer event's timeStamp that started
  * the gesture resolution sequence.
  * @param {Object} aPoints An existing set of points (from previous events).
  * @param {?String} aLastEvent Last pointer event type.
  */
 function TripleTap(aTimeStamp, aPoints, aLastEvent) {
+  this._inProgress = true;
   TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, DoubleTapHold);
 }
 
 TripleTap.prototype = Object.create(TapGesture.prototype);
 TripleTap.prototype.type = 'tripletap';
 
 /**
  * Common base object for gestures that are created as resolved.
--- a/accessible/tests/mochitest/jsat/dom_helper.js
+++ b/accessible/tests/mochitest/jsat/dom_helper.js
@@ -97,32 +97,36 @@ sendTouchEvent.touchList = null;
 var eventMap = {
   touchstart: sendTouchEvent,
   touchend: sendTouchEvent,
   touchmove: sendTouchEvent
 };
 
 var originalDwellThreshold = GestureSettings.dwellThreshold;
 var originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
+var originalConsecutiveGestureDelay =
+  GestureSettings.maxConsecutiveGestureDelay;
 
 /**
  * Attach a listener for the mozAccessFuGesture event that tests its
  * type.
  * @param  {Array} aExpectedGestures A stack of expected event types.
  * Note: the listener is removed once the stack reaches 0.
  */
 function testMozAccessFuGesture(aExpectedGestures) {
   var types = aExpectedGestures;
   function handleGesture(aEvent) {
     if (aEvent.detail.type !== types[0].type) {
+      info('Got ' + aEvent.detail.type + ' waiting for ' + types[0].type);
       // The is not the event of interest.
       return;
     }
     is(!!aEvent.detail.edge, !!types[0].edge);
-    ok(true, 'Received correct mozAccessFuGesture: ' + types.shift() + '.');
+    ok(true, 'Received correct mozAccessFuGesture: ' +
+      JSON.stringify(types.shift()) + '.');
     if (types.length === 0) {
       win.removeEventListener('mozAccessFuGesture', handleGesture);
       if (AccessFuTest.sequenceCleanup) {
         AccessFuTest.sequenceCleanup();
       }
       AccessFuTest.nextTest();
     }
   }
@@ -168,16 +172,19 @@ AccessFuTest.addSequence = function Acce
     var events = aSequence.events;
     function fireEvent(aEvent) {
       var event = {
         points: convertPointCoordinates(aEvent.points),
         type: aEvent.type
       };
       var timeStamp = Date.now();
       resetTimers();
+      GestureSettings.maxConsecutiveGestureDelay =
+        aEvent.removeConsecutiveGestureDelay ?
+        0 : originalConsecutiveGestureDelay;
       GestureTracker.handle(event, timeStamp);
       setTimers(timeStamp, aEvent.removeDwellThreshold,
         aEvent.removeSwipeMaxDuration);
       processEvents();
     }
     function processEvents() {
       if (events.length === 0) {
         return;
--- a/accessible/tests/mochitest/jsat/gestures.json
+++ b/accessible/tests/mochitest/jsat/gestures.json
@@ -1,22 +1,24 @@
 [
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
-      {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+      {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+       "removeConsecutiveGestureDelay": true }
     ],
     "expectedGestures": [{ "type": "tap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]},
-      {"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]}
+      {"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}],
+       "removeConsecutiveGestureDelay": true }
     ],
     "expectedGestures": [{ "type": "tap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
@@ -29,54 +31,55 @@
       {"type": "pointermove",
         "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
       {"type": "pointerup",
         "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove",
         "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]},
       {"type": "pointerup",
-        "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]}
+        "points": [{"x": 0.97, "y": 1.01, "identifier": 1}],
+        "removeConsecutiveGestureDelay": true }
     ],
-    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }]
+    "expectedGestures": [{ "type": "doubletap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
-      {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+      {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+       "removeConsecutiveGestureDelay": true }
     ],
-    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }, { "type": "tripletap" }]
+    "expectedGestures": [{ "type": "tripletap" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": [{ "type": "tap" }, { "type": "doubletap" },
-      { "type": "doubletaphold" }, { "type": "doubletapholdend" }]
+    "expectedGestures": [{ "type": "doubletaphold" },
+      { "type": "doubletapholdend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
         "removeDwellThreshold": true},
       {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
     ],
-    "expectedGestures": [{ "type": "tap" }, { "type": "taphold" },
-      { "type": "tapholdend" }]
+    "expectedGestures": [{ "type": "taphold" }, { "type": "tapholdend" }]
   },
   {
     "events": [
       {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
       {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
       {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
     ],
     "expectedGestures": [{ "type": "swiperight" }]
--- a/accessible/tests/mochitest/jsat/test_gesture_tracker.html
+++ b/accessible/tests/mochitest/jsat/test_gesture_tracker.html
@@ -26,17 +26,17 @@
     function doTest() {
       loadJSON("./gestures.json", function onSuccess(gestures) {
         AccessFuTest.addFunc(startGestureTracker);
         AccessFuTest.sequenceCleanup = GestureTracker.reset.bind(
           GestureTracker);
         gestures.forEach(AccessFuTest.addSequence);
         AccessFuTest.addFunc(stopGestureTracker);
         AccessFuTest.waitForExplicitFinish();
-        Logger.logLevel = Logger.DEBUG;
+        Logger.logLevel = Logger.GESTURE;
         AccessFuTest.runTests();
       });
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -560,17 +560,21 @@ InitXPCOMGlue(const char *argv0, nsIFile
   }
 
   rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
   if (NS_FAILED(rv)) {
     Output("Couldn't load XRE functions.\n");
     return rv;
   }
 
+#ifndef MOZ_METRO
+  // This will set this thread as the main thread, which in metro land is
+  // wrong. We initialize this later from the right thread in nsAppRunner.
   NS_LogInit();
+#endif
 
   // chop XPCOM_DLL off exePath
   *lastSlash = '\0';
 #ifdef XP_MACOSX
   lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
   strcpy(lastSlash + 1, kOSXResourcesFolder);
 #endif
 #ifdef XP_WIN
--- a/browser/metro/shell/commandexecutehandler/Makefile.in
+++ b/browser/metro/shell/commandexecutehandler/Makefile.in
@@ -1,11 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+ifndef MOZ_WINCONSOLE
+MOZ_WINCONSOLE = 0
+endif
+
 include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/config/rules.mk
 
 DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)
 
 # Don't link against mozglue.dll
 MOZ_GLUE_LDFLAGS =
 MOZ_GLUE_PROGRAM_LDFLAGS =
--- a/content/base/public/nsHostObjectProtocolHandler.h
+++ b/content/base/public/nsHostObjectProtocolHandler.h
@@ -18,16 +18,17 @@
 #define RTSPURI_SCHEME "rtsp"
 
 class nsIDOMBlob;
 class nsIPrincipal;
 
 namespace mozilla {
 class DOMMediaStream;
 namespace dom {
+class FileImpl;
 class MediaSource;
 }
 }
 
 class nsHostObjectProtocolHandler : public nsIProtocolHandler
 {
 public:
   nsHostObjectProtocolHandler();
@@ -114,16 +115,19 @@ inline bool IsMediaSourceURI(nsIURI* aUr
 
 inline bool IsFontTableURI(nsIURI* aUri)
 {
   bool isFont;
   return NS_SUCCEEDED(aUri->SchemeIs(FONTTABLEURI_SCHEME, &isFont)) && isFont;
 }
 
 extern nsresult
+NS_GetBlobForBlobURI(nsIURI* aURI, mozilla::dom::FileImpl** aBlob);
+
+extern nsresult
 NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
 
 extern nsresult
 NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream);
 
 extern nsresult
 NS_GetSourceForMediaSourceURI(nsIURI* aURI, mozilla::dom::MediaSource** aSource);
 
--- a/content/base/src/nsHostObjectProtocolHandler.cpp
+++ b/content/base/src/nsHostObjectProtocolHandler.cpp
@@ -583,29 +583,42 @@ nsMediaSourceProtocolHandler::GetScheme(
 NS_IMETHODIMP
 nsFontTableProtocolHandler::GetScheme(nsACString &result)
 {
   result.AssignLiteral(FONTTABLEURI_SCHEME);
   return NS_OK;
 }
 
 nsresult
-NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
+NS_GetBlobForBlobURI(nsIURI* aURI, FileImpl** aBlob)
 {
   NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
 
-  *aStream = nullptr;
+  *aBlob = nullptr;
 
   nsCOMPtr<PIFileImpl> blobImpl = do_QueryInterface(GetDataObject(aURI));
   if (!blobImpl) {
     return NS_ERROR_DOM_BAD_URI;
   }
 
-  FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
-  return blob->GetInternalStream(aStream);
+  nsRefPtr<FileImpl> blob = static_cast<FileImpl*>(blobImpl.get());
+  blob.forget(aBlob);
+  return NS_OK;
+}
+
+nsresult
+NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
+{
+  nsRefPtr<FileImpl> blobImpl;
+  nsresult rv = NS_GetBlobForBlobURI(aURI, getter_AddRefs(blobImpl));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return blobImpl->GetInternalStream(aStream);
 }
 
 nsresult
 NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream)
 {
   NS_ASSERTION(IsMediaStreamURI(aURI), "Only call this with mediastream URIs");
 
   *aStream = nullptr;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -201,16 +201,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
 #include "TimeChangeObserver.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/Console.h"
+#include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/MozSelfSupportBinding.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/PopupBlockedEvent.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
@@ -6363,18 +6364,17 @@ nsGlobalWindow::Confirm(const nsAString&
 
   return rv.ErrorCode();
 }
 
 already_AddRefed<Promise>
 nsGlobalWindow::Fetch(const RequestOrScalarValueString& aInput,
                       const RequestInit& aInit, ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  return FetchRequest(this, aInput, aInit, aRv);
 }
 
 void
 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
                        nsAString& aReturn, ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
   // sure any modifications here don't need to happen over there!
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -168,16 +168,17 @@ static bool sHasRunGC;
 // have an easy place to know when a load ends or is interrupted in
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static uint32_t sPendingLoadCount;
 static bool sLoadingInProgress;
 
 static uint32_t sCCollectedWaitingForGC;
+static uint32_t sCCollectedZonesWaitingForGC;
 static uint32_t sLikelyShortLivingObjectsNeedingGC;
 static bool sPostGCEventsToConsole;
 static bool sPostGCEventsToObserver;
 static int32_t sCCTimerFireCount = 0;
 static uint32_t sMinForgetSkippableTime = UINT32_MAX;
 static uint32_t sMaxForgetSkippableTime = 0;
 static uint32_t sTotalForgetSkippableTime = 0;
 static uint32_t sRemovedPurples = 0;
@@ -241,16 +242,17 @@ KillTimers()
 }
 
 // If we collected a substantial amount of cycles, poke the GC since more objects
 // might be unreachable now.
 static bool
 NeedsGCAfterCC()
 {
   return sCCollectedWaitingForGC > 250 ||
+    sCCollectedZonesWaitingForGC > 0 ||
     sLikelyShortLivingObjectsNeedingGC > 2500 ||
     sNeedsGCAfterCC;
 }
 
 class nsJSEnvironmentObserver MOZ_FINAL : public nsIObserver
 {
   ~nsJSEnvironmentObserver() {}
 public:
@@ -1801,17 +1803,18 @@ nsJSContext::EndCycleCollectionCallback(
 
   nsJSContext::KillICCTimer();
 
   // Update timing information for the current slice before we log it, if
   // we previously called PrepareForCycleCollectionSlice(). During shutdown
   // CCs, this won't happen.
   gCCStats.FinishCycleCollectionSlice();
 
-  sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed;
+  sCCollectedWaitingForGC += aResults.mFreedGCed;
+  sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
 
   TimeStamp endCCTimeStamp = TimeStamp::Now();
   uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
 
   if (NeedsGCAfterCC()) {
     PokeGC(JS::gcreason::CC_WAITING,
            NS_GC_DELAY - std::min(ccNowDuration, kMaxICCDuration));
   }
@@ -1850,25 +1853,25 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
+      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
       MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
-                                        sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
+                                        sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
                                         gcMsg.get(),
                                         sForgetSkippableBeforeCC,
                                         minForgetSkippableTime / PR_USEC_PER_MSEC,
                                         sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
                                         (sTotalForgetSkippableTime / cleanups) /
                                           PR_USEC_PER_MSEC,
                                         sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
                                         gCCStats.mMaxSkippableDuration, sRemovedPurples));
@@ -1890,16 +1893,17 @@ nsJSContext::EndCycleCollectionCallback(
          MOZ_UTF16("\"suspected\": %lu, ")
          MOZ_UTF16("\"visited\": { ")
              MOZ_UTF16("\"RCed\": %lu, ")
              MOZ_UTF16("\"GCed\": %lu }, ")
          MOZ_UTF16("\"collected\": { ")
              MOZ_UTF16("\"RCed\": %lu, ")
              MOZ_UTF16("\"GCed\": %lu }, ")
          MOZ_UTF16("\"waiting_for_gc\": %lu, ")
+         MOZ_UTF16("\"zones_waiting_for_gc\": %lu, ")
          MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
          MOZ_UTF16("\"forced_gc\": %d, ")
          MOZ_UTF16("\"forget_skippable\": { ")
              MOZ_UTF16("\"times_before_cc\": %lu, ")
              MOZ_UTF16("\"min\": %lu, ")
              MOZ_UTF16("\"max\": %lu, ")
              MOZ_UTF16("\"avg\": %lu, ")
              MOZ_UTF16("\"total\": %lu, ")
@@ -1910,16 +1914,17 @@ nsJSContext::EndCycleCollectionCallback(
                                          gCCStats.mMaxSliceTime,
                                          gCCStats.mTotalSliceTime,
                                          gCCStats.mMaxGCDuration,
                                          gCCStats.mMaxSkippableDuration,
                                          gCCStats.mSuspected,
                                          aResults.mVisitedRefCounted, aResults.mVisitedGCed,
                                          aResults.mFreedRefCounted, aResults.mFreedGCed,
                                          sCCollectedWaitingForGC,
+                                         sCCollectedZonesWaitingForGC,
                                          sLikelyShortLivingObjectsNeedingGC,
                                          aResults.mForcedGC,
                                          sForgetSkippableBeforeCC,
                                          minForgetSkippableTime / PR_USEC_PER_MSEC,
                                          sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
                                          (sTotalForgetSkippableTime / cleanups) /
                                            PR_USEC_PER_MSEC,
                                          sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
@@ -2365,16 +2370,17 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
     }
   }
 
   if (aProgress == JS::GC_CYCLE_END) {
     // May need to kill the inter-slice GC timer
     nsJSContext::KillInterSliceGCTimer();
 
     sCCollectedWaitingForGC = 0;
+    sCCollectedZonesWaitingForGC = 0;
     sLikelyShortLivingObjectsNeedingGC = 0;
     sCleanupsSinceLastGC = 0;
     sNeedsFullCC = true;
     sHasRunGC = true;
     nsJSContext::MaybePokeCC();
 
     if (aDesc.isCompartment_) {
       if (!sFullGCTimer && !sShuttingDown) {
@@ -2447,16 +2453,17 @@ mozilla::dom::StartupJSEnvironment()
   sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
   sCCLockedOut = false;
   sCCLockedOutTime = 0;
   sLastCCEndTime = TimeStamp();
   sHasRunGC = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
+  sCCollectedZonesWaitingForGC = 0;
   sLikelyShortLivingObjectsNeedingGC = 0;
   sPostGCEventsToConsole = false;
   sNeedsFullCC = false;
   sNeedsGCAfterCC = false;
   gNameSpaceManager = nullptr;
   sRuntimeService = nullptr;
   sRuntime = nullptr;
   sIsInitialized = false;
--- a/dom/bluetooth/tests/marionette/head.js
+++ b/dom/bluetooth/tests/marionette/head.js
@@ -88,43 +88,16 @@ function runEmulatorCmdSafe(aCommand) {
       deferred.reject(aResult);
     }
   });
 
   return deferred.promise;
 }
 
 /**
- * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
- *
- * Fulfill params: A DOMEvent.
- * Reject params: A DOMEvent.
- *
- * @param aRequest
- *        A DOMRequest instance.
- *
- * @return A deferred promise.
- */
-function wrapDomRequestAsPromise(aRequest) {
-  let deferred = Promise.defer();
-
-  ok(aRequest instanceof DOMRequest,
-     "aRequest is instanceof " + aRequest.constructor);
-
-  aRequest.onsuccess = function(aEvent) {
-    deferred.resolve(aEvent);
-  };
-  aRequest.onerror = function(aEvent) {
-    deferred.reject(aEvent);
-  };
-
-  return deferred.promise;
-}
-
-/**
  * Add a Bluetooth remote device to scatternet and set its properties.
  *
  * Use QEMU command 'bt remote add' to add a virtual Bluetooth remote
  * and set its properties by setEmulatorDeviceProperty().
  *
  * Fulfill params:
  *   result -- bluetooth address of the remote device.
  * Reject params: (none)
@@ -241,26 +214,25 @@ function getEmulatorDeviceProperty(aAddr
  * @param aAdapter
  *        A BluetoothAdapter which is used to interact with local BT device.
  *
  * @return A deferred promise.
  */
 function startDiscovery(aAdapter) {
   let request = aAdapter.startDiscovery();
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve() {
+  return request.then(function resolve() {
       // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
       //     Currently, discovering state wouldn't change immediately here.
       //     We would turn on this check when the redesigned API are landed.
       // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
       log("  Start discovery - Success");
-    }, function reject(aEvent) {
+    }, function reject(aError) {
       ok(false, "Start discovery - Fail");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Stop discovering Bluetooth devices.
  *
  * Allows the device's adapter to stop seeking for remote devices.
  *
@@ -270,26 +242,25 @@ function startDiscovery(aAdapter) {
  * @param aAdapter
  *        A BluetoothAdapter which is used to interact with local BT device.
  *
  * @return A deferred promise.
  */
 function stopDiscovery(aAdapter) {
   let request = aAdapter.stopDiscovery();
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve() {
+  return request.then(function resolve() {
       // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
       //     Currently, discovering state wouldn't change immediately here.
       //     We would turn on this check when the redesigned API are landed.
       // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
       log("  Stop discovery - Success");
-    }, function reject(aEvent) {
+    }, function reject(aError) {
       ok(false, "Stop discovery - Fail");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Wait for 'devicefound' event of specified devices.
  *
  * Resolve if that every specified devices has been found.  Never reject.
  *
@@ -358,22 +329,21 @@ function startDiscoveryAndWaitDevicesFou
  * @param aDeviceAddress
  *        The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
  *
  * @return A deferred promise.
  */
 function pair(aAdapter, aDeviceAddress) {
   let request = aAdapter.pair(aDeviceAddress);
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve() {
+  return request.then(function resolve() {
       log("  Pair - Success");
-    }, function reject(aEvent) {
+    }, function reject(aError) {
       ok(false, "Pair - Fail");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Remove the paired device from the paired device list.
  *
  * Remove the paired device from the paired device list of the device's adapter.
  *
@@ -385,22 +355,21 @@ function pair(aAdapter, aDeviceAddress) 
  * @param aDeviceAddress
  *        The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
  *
  * @return A deferred promise.
  */
 function unpair(aAdapter, aDeviceAddress) {
   let request = aAdapter.unpair(aDeviceAddress);
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve() {
+  return request.then(function resolve() {
       log("  Unpair - Success");
-    }, function reject(aEvent) {
+    }, function reject(aError) {
       ok(false, "Unpair - Fail");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Start pairing a remote device and wait for "pairedstatuschanged" event.
  *
  * Start pairing a remote device with the device's adapter and wait for
  * "pairedstatuschanged" event.
@@ -435,24 +404,23 @@ function pairDeviceAndWait(aAdapter, aDe
  * @param aAdapter
  *        A BluetoothAdapter which is used to interact with local BT device.
  *
  * @return A deferred promise.
  */
 function getPairedDevices(aAdapter) {
   let request = aAdapter.getPairedDevices();
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve() {
+  return request.then(function resolve() {
       log("  getPairedDevices - Success");
       let pairedDevices = request.result.slice();
       return pairedDevices;
-    }, function reject(aEvent) {
+    }, function reject(aError) {
       ok(false, "getPairedDevices - Fail");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Get mozSettings value specified by @aKey.
  *
  * Resolve if that mozSettings value is retrieved successfully, reject
  * otherwise.
@@ -464,23 +432,22 @@ function getPairedDevices(aAdapter) {
  * @param aKey
  *        A string.
  *
  * @return A deferred promise.
  */
 function getSettings(aKey) {
   let request = navigator.mozSettings.createLock().get(aKey);
 
-  return wrapDomRequestAsPromise(request)
-    .then(function resolve(aEvent) {
+  return request.then(function resolve(aValue) {
       ok(true, "getSettings(" + aKey + ")");
-      return aEvent.target.result[aKey];
-    }, function reject(aEvent) {
+      return aValue[aKey];
+    }, function reject(aError) {
       ok(false, "getSettings(" + aKey + ")");
-      throw aEvent.target.error;
+      throw aError;
     });
 }
 
 /**
  * Set mozSettings values.
  *
  * Resolve if that mozSettings value is set successfully, reject otherwise.
  *
--- a/dom/datastore/DataStoreChangeNotifier.jsm
+++ b/dom/datastore/DataStoreChangeNotifier.jsm
@@ -100,30 +100,18 @@ this.DataStoreChangeNotifier = {
       delete this.sysMsgOnChangeShortTimers[storeKey];
     }
     var longTimer = this.sysMsgOnChangeLongTimers[storeKey];
     if (longTimer) {
       longTimer.cancel();
       delete this.sysMsgOnChangeLongTimers[storeKey];
     }
 
-    // Get all the manifest URLs of the apps which can access the datastore.
-    var manifestURLs = dataStoreService.getAppManifestURLsForDataStore(aStore);
-    var enumerate = manifestURLs.enumerate();
-    while (enumerate.hasMoreElements()) {
-      var manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
-      debug("Notify app " + manifestURL + " of datastore updates");
-      // Send the system message 'datastore-update-{store name}' to all the
-      // pages for these apps. With the manifest URL of the owner in the message
-      // payload, it notifies the consumer a sync operation should be performed.
-      systemMessenger.sendMessage("datastore-update-" + aStore,
-                                  { owner: aOwner },
-                                  null,
-                                  Services.io.newURI(manifestURL, null, null));
-    }
+    systemMessenger.broadcastMessage("datastore-update-" + aStore,
+                                     { owner: aOwner });
   },
 
   // Use the following logic to broadcast system messages in a moderate pattern.
   // 1. When an entry is changed, start a short timer and a long timer.
   // 2. If an entry is changed while the short timer is running, reset it.
   //    Do not reset the long timer.
   // 3. Once either fires, broadcast the system message and cancel both timers.
   setSystemMessageTimeout: function(aStore, aOwner) {
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -1,36 +1,286 @@
 /* -*- Mode: C++; 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/. */
 
 #include "Fetch.h"
 
+#include "nsIDocument.h"
+#include "nsIGlobalObject.h"
 #include "nsIStringStream.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIUnicodeEncoder.h"
 
 #include "nsDOMString.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/FetchDriver.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/dom/workers/Workers.h"
+
+#include "InternalResponse.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
 
 namespace mozilla {
 namespace dom {
 
+using namespace workers;
+
+class WorkerFetchResolver MOZ_FINAL : public FetchDriverObserver
+{
+  friend class WorkerFetchResponseRunnable;
+  friend class ResolveFetchWithBodyRunnable;
+
+  // This promise proxy is for the Promise returned by a call to fetch() that
+  // is resolved with a Response instance.
+  nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
+  // Passed from main thread to worker thread after being initialized (except
+  // for the body.
+  nsRefPtr<InternalResponse> mInternalResponse;
+public:
+
+  WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise);
+
+  void
+  OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
+
+  workers::WorkerPrivate*
+  GetWorkerPrivate() { return mPromiseProxy->GetWorkerPrivate(); }
+
+private:
+  ~WorkerFetchResolver();
+};
+
+class MainThreadFetchResolver MOZ_FINAL : public FetchDriverObserver
+{
+  nsRefPtr<Promise> mPromise;
+  nsRefPtr<InternalResponse> mInternalResponse;
+
+  NS_DECL_OWNINGTHREAD
+public:
+  MainThreadFetchResolver(Promise* aPromise);
+
+  void
+  OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
+
+private:
+  ~MainThreadFetchResolver();
+};
+
+class MainThreadFetchRunnable : public nsRunnable
+{
+  nsRefPtr<WorkerFetchResolver> mResolver;
+  nsRefPtr<InternalRequest> mRequest;
+
+public:
+  MainThreadFetchRunnable(WorkerPrivate* aWorkerPrivate,
+                          Promise* aPromise,
+                          InternalRequest* aRequest)
+    : mResolver(new WorkerFetchResolver(aWorkerPrivate, aPromise))
+    , mRequest(aRequest)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsOnMainThread();
+    nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest);
+    nsresult rv = fetch->Fetch(mResolver);
+    // Right now we only support async fetch, which should never directly fail.
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    return NS_OK;
+  }
+};
+
+already_AddRefed<Promise>
+FetchRequest(nsIGlobalObject* aGlobal, const RequestOrScalarValueString& aInput,
+             const RequestInit& aInit, ErrorResult& aRv)
+{
+  nsRefPtr<Promise> p = Promise::Create(aGlobal, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  AutoJSAPI jsapi;
+  jsapi.Init(aGlobal);
+  JSContext* cx = jsapi.cx();
+
+  JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
+  GlobalObject global(cx, jsGlobal);
+
+  nsRefPtr<Request> request = Request::Constructor(global, aInput, aInit, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  nsRefPtr<InternalRequest> r = request->GetInternalRequest();
+  if (!r->ReferrerIsNone()) {
+    nsAutoCString ref;
+    aRv = GetRequestReferrer(aGlobal, r, ref);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+    r->SetReferrer(ref);
+  }
+
+  if (NS_IsMainThread()) {
+    nsRefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
+    nsRefPtr<FetchDriver> fetch = new FetchDriver(r);
+    aRv = fetch->Fetch(resolver);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  } else {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(worker, p, r);
+    if (NS_FAILED(NS_DispatchToMainThread(run))) {
+      NS_WARNING("MainThreadFetchRunnable dispatch failed!");
+    }
+  }
+
+  return p.forget();
+}
+
+MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
+  : mPromise(aPromise)
+{
+}
+
+void
+MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
+{
+  NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
+  AssertIsOnMainThread();
+  mInternalResponse = aResponse;
+
+  nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
+
+  nsRefPtr<Response> response = new Response(go, aResponse);
+  mPromise->MaybeResolve(response);
+}
+
+MainThreadFetchResolver::~MainThreadFetchResolver()
+{
+  NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
+}
+
+class WorkerFetchResponseRunnable : public WorkerRunnable
+{
+  nsRefPtr<WorkerFetchResolver> mResolver;
+public:
+  WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver)
+    : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
+    , mResolver(aResolver)
+  {
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
+
+    nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
+    nsRefPtr<Response> response = new Response(global, mResolver->mInternalResponse);
+
+    nsRefPtr<Promise> promise = mResolver->mPromiseProxy->GetWorkerPromise();
+    MOZ_ASSERT(promise);
+    promise->MaybeResolve(response);
+
+    mResolver->mPromiseProxy->CleanUp(aCx);
+    return true;
+  }
+};
+
+WorkerFetchResolver::WorkerFetchResolver(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
+  : mPromiseProxy(new PromiseWorkerProxy(aWorkerPrivate, aPromise))
+{
+}
+
+WorkerFetchResolver::~WorkerFetchResolver()
+{
+}
+
+void
+WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
+{
+  AssertIsOnMainThread();
+  mInternalResponse = aResponse;
+
+  nsRefPtr<WorkerFetchResponseRunnable> r =
+    new WorkerFetchResponseRunnable(this);
+
+  AutoSafeJSContext cx;
+  if (!r->Dispatch(cx)) {
+    NS_WARNING("Could not dispatch fetch resolve");
+  }
+}
+
+// Empty string for no-referrer. FIXME(nsm): Does returning empty string
+// actually lead to no-referrer in the base channel?
+// The actual referrer policy and stripping is dealt with by HttpBaseChannel,
+// this always returns the full API referrer URL of the relevant global.
+nsresult
+GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer)
+{
+  if (aRequest->ReferrerIsURL()) {
+    aReferrer = aRequest->ReferrerAsURL();
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
+  if (window) {
+    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+    if (doc) {
+      nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
+      nsAutoCString origin;
+      nsresult rv = nsContentUtils::GetASCIIOrigin(docURI, origin);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      nsAutoString referrer;
+      doc->GetReferrer(referrer);
+      aReferrer = NS_ConvertUTF16toUTF8(referrer);
+    }
+  } else {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+    aReferrer = worker->GetLocationInfo().mHref;
+    // XXX(nsm): Algorithm says "If source is not a URL..." but when is it
+    // not a URL?
+  }
+
+  return NS_OK;
+}
+
 namespace {
 nsresult
 ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
                        nsIInputStream** aStream)
 {
   aBuffer.ComputeLengthAndData();
   //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
   return NS_NewByteInputStream(aStream,
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -5,25 +5,40 @@
 
 #ifndef mozilla_dom_Fetch_h
 #define mozilla_dom_Fetch_h
 
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsString.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/dom/RequestBinding.h"
 
 class nsIInputStream;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams;
+class InternalRequest;
 class OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams;
 class Promise;
+class RequestOrScalarValueString;
+
+namespace workers {
+class WorkerPrivate;
+} // namespace workers
+
+already_AddRefed<Promise>
+FetchRequest(nsIGlobalObject* aGlobal, const RequestOrScalarValueString& aInput,
+             const RequestInit& aInit, ErrorResult& aRv);
+
+nsresult
+GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer);
 
 /*
  * Creates an nsIInputStream based on the fetch specifications 'extract a byte
  * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
  * Stores content type in out param aContentType.
  */
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams& aBodyInit,
@@ -100,12 +115,13 @@ private:
   }
 
   already_AddRefed<Promise>
   ConsumeBody(ConsumeType aType, ErrorResult& aRv);
 
   bool mBodyUsed;
   nsCString mMimeType;
 };
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Fetch_h
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchDriver.cpp
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; 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/. */
+
+#include "mozilla/dom/FetchDriver.h"
+
+#include "nsIScriptSecurityManager.h"
+
+#include "nsContentPolicyUtils.h"
+#include "nsDataHandler.h"
+#include "nsHostObjectProtocolHandler.h"
+#include "nsNetUtil.h"
+#include "nsStringStream.h"
+
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/workers/Workers.h"
+
+#include "Fetch.h"
+#include "InternalRequest.h"
+#include "InternalResponse.h"
+
+namespace mozilla {
+namespace dom {
+
+FetchDriver::FetchDriver(InternalRequest* aRequest)
+  : mRequest(aRequest)
+  , mFetchRecursionCount(0)
+{
+}
+
+FetchDriver::~FetchDriver()
+{
+}
+
+nsresult
+FetchDriver::Fetch(FetchDriverObserver* aObserver)
+{
+  workers::AssertIsOnMainThread();
+  mObserver = aObserver;
+
+  return Fetch(false /* CORS flag */);
+}
+
+nsresult
+FetchDriver::Fetch(bool aCORSFlag)
+{
+  // We do not currently implement parts of the spec that lead to recursion.
+  MOZ_ASSERT(mFetchRecursionCount == 0);
+  mFetchRecursionCount++;
+
+  // FIXME(nsm): Deal with HSTS.
+
+  if (!mRequest->IsSynchronous() && mFetchRecursionCount <= 1) {
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethodWithArg<bool>(this, &FetchDriver::ContinueFetch, aCORSFlag);
+    return NS_DispatchToCurrentThread(r);
+  }
+
+  MOZ_CRASH("Synchronous fetch not supported");
+}
+
+nsresult
+FetchDriver::ContinueFetch(bool aCORSFlag)
+{
+  workers::AssertIsOnMainThread();
+
+  nsAutoCString url;
+  mRequest->GetURL(url);
+  nsCOMPtr<nsIURI> requestURI;
+  // FIXME(nsm): Deal with relative URLs.
+  nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
+                          nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FailWithNetworkError();
+  }
+
+  // FIXME(nsm): Bug 1039846: Add CSP checks
+
+  nsAutoCString scheme;
+  rv = requestURI->GetScheme(scheme);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FailWithNetworkError();
+  }
+
+  nsAutoCString originURL;
+  mRequest->GetOrigin(originURL);
+  nsCOMPtr<nsIURI> originURI;
+  rv = NS_NewURI(getter_AddRefs(originURI), originURL, nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return FailWithNetworkError();
+  }
+
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  rv = ssm->CheckSameOriginURI(requestURI, originURI, false);
+  if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
+      (scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
+      scheme.EqualsLiteral("about")) {
+    return BasicFetch();
+  }
+
+  if (mRequest->Mode() == RequestMode::Same_origin) {
+    return FailWithNetworkError();
+  }
+
+  if (mRequest->Mode() == RequestMode::No_cors) {
+    mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
+    return BasicFetch();
+  }
+
+  if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
+    return FailWithNetworkError();
+  }
+
+  if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
+      (mRequest->UnsafeRequest() && (mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
+    // FIXME(nsm): Set corsPreflight;
+  }
+
+  mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
+  // FIXME(nsm): HttpFetch.
+  return FailWithNetworkError();
+}
+
+nsresult
+FetchDriver::BasicFetch()
+{
+  nsAutoCString url;
+  mRequest->GetURL(url);
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri),
+                 url,
+                 nullptr,
+                 nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString scheme;
+  rv = uri->GetScheme(scheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (scheme.LowerCaseEqualsLiteral("about")) {
+    if (url.EqualsLiteral("about:blank")) {
+      nsRefPtr<InternalResponse> response =
+        new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
+      ErrorResult result;
+      response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
+                                  NS_LITERAL_CSTRING("text/html;charset=utf-8"),
+                                  result);
+      MOZ_ASSERT(!result.Failed());
+      nsCOMPtr<nsIInputStream> body;
+      rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return FailWithNetworkError();
+      }
+
+      response->SetBody(body);
+      BeginResponse(response);
+      return SucceedWithResponse();
+    }
+    return FailWithNetworkError();
+  }
+
+  if (scheme.LowerCaseEqualsLiteral("blob")) {
+    nsRefPtr<FileImpl> blobImpl;
+    rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
+    FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return FailWithNetworkError();
+    }
+
+    nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
+    {
+      ErrorResult result;
+      uint64_t size = blob->GetSize(result);
+      if (NS_WARN_IF(result.Failed())) {
+        return FailWithNetworkError();
+      }
+
+      nsAutoString sizeStr;
+      sizeStr.AppendInt(size);
+      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
+      if (NS_WARN_IF(result.Failed())) {
+        return FailWithNetworkError();
+      }
+
+      nsAutoString type;
+      blob->GetType(type);
+      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
+      if (NS_WARN_IF(result.Failed())) {
+        return FailWithNetworkError();
+      }
+    }
+
+    nsCOMPtr<nsIInputStream> stream;
+    rv = blob->GetInternalStream(getter_AddRefs(stream));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return FailWithNetworkError();
+    }
+
+    response->SetBody(stream);
+    BeginResponse(response);
+    return SucceedWithResponse();
+  }
+
+  if (scheme.LowerCaseEqualsLiteral("data")) {
+    nsAutoCString method;
+    mRequest->GetMethod(method);
+    if (method.LowerCaseEqualsASCII("get")) {
+      // Use nsDataHandler directly so that we can extract the content type.
+      // XXX(nsm): Is there a way to acquire the charset without such tight
+      // coupling with the DataHandler? nsIProtocolHandler does not provide
+      // anything similar.
+      nsAutoCString contentType, contentCharset, dataBuffer, hashRef;
+      bool isBase64;
+      rv = nsDataHandler::ParseURI(url,
+                                   contentType,
+                                   contentCharset,
+                                   isBase64,
+                                   dataBuffer,
+                                   hashRef);
+      if (NS_SUCCEEDED(rv)) {
+        ErrorResult result;
+        nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
+        if (!contentCharset.IsEmpty()) {
+          contentType.Append(";charset=");
+          contentType.Append(contentCharset);
+        }
+
+        response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
+        if (!result.Failed()) {
+          nsCOMPtr<nsIInputStream> stream;
+          rv = NS_NewCStringInputStream(getter_AddRefs(stream), dataBuffer);
+          if (NS_SUCCEEDED(rv)) {
+            response->SetBody(stream);
+            BeginResponse(response);
+            return SucceedWithResponse();
+          }
+        }
+      }
+    }
+
+    return FailWithNetworkError();
+  }
+
+  if (scheme.LowerCaseEqualsLiteral("file")) {
+  } else if (scheme.LowerCaseEqualsLiteral("http") ||
+             scheme.LowerCaseEqualsLiteral("https")) {
+    // FIXME(nsm): HttpFetch.
+    return FailWithNetworkError();
+  }
+
+  return FailWithNetworkError();
+}
+
+nsresult
+FetchDriver::BeginResponse(InternalResponse* aResponse)
+{
+  MOZ_ASSERT(aResponse);
+  nsAutoCString reqURL;
+  mRequest->GetURL(reqURL);
+  aResponse->SetUrl(reqURL);
+
+  // FIXME(nsm): Handle mixed content check, step 7 of fetch.
+
+  nsRefPtr<InternalResponse> filteredResponse;
+  switch (mRequest->GetResponseTainting()) {
+    case InternalRequest::RESPONSETAINT_BASIC:
+      filteredResponse = InternalResponse::BasicResponse(aResponse);
+      break;
+    case InternalRequest::RESPONSETAINT_CORS:
+      filteredResponse = InternalResponse::CORSResponse(aResponse);
+      break;
+    case InternalRequest::RESPONSETAINT_OPAQUE:
+      filteredResponse = InternalResponse::OpaqueResponse();
+      break;
+    default:
+      MOZ_CRASH("Unexpected case");
+  }
+
+  MOZ_ASSERT(filteredResponse);
+  mObserver->OnResponseAvailable(filteredResponse);
+  return NS_OK;
+}
+
+nsresult
+FetchDriver::SucceedWithResponse()
+{
+  return NS_OK;
+}
+
+nsresult
+FetchDriver::FailWithNetworkError()
+{
+  nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
+  mObserver->OnResponseAvailable(error);
+  // FIXME(nsm): Some sort of shutdown?
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/fetch/FetchDriver.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef mozilla_dom_FetchDriver_h
+#define mozilla_dom_FetchDriver_h
+
+#include "nsIStreamListener.h"
+#include "nsRefPtr.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class InternalRequest;
+class InternalResponse;
+
+class FetchDriverObserver
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver);
+  virtual void OnResponseAvailable(InternalResponse* aResponse) = 0;
+
+protected:
+  virtual ~FetchDriverObserver()
+  { };
+};
+
+class FetchDriver MOZ_FINAL
+{
+  NS_INLINE_DECL_REFCOUNTING(FetchDriver)
+public:
+  explicit FetchDriver(InternalRequest* aRequest);
+  NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
+
+private:
+  nsRefPtr<InternalRequest> mRequest;
+  nsRefPtr<FetchDriverObserver> mObserver;
+  uint32_t mFetchRecursionCount;
+
+  FetchDriver() MOZ_DELETE;
+  FetchDriver(const FetchDriver&) MOZ_DELETE;
+  FetchDriver& operator=(const FetchDriver&) MOZ_DELETE;
+  ~FetchDriver();
+
+  nsresult Fetch(bool aCORSFlag);
+  nsresult ContinueFetch(bool aCORSFlag);
+  nsresult BasicFetch();
+  nsresult FailWithNetworkError();
+  nsresult BeginResponse(InternalResponse* aResponse);
+  nsresult SucceedWithResponse();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FetchDriver_h
--- a/dom/fetch/InternalHeaders.cpp
+++ b/dom/fetch/InternalHeaders.cpp
@@ -263,10 +263,66 @@ void
 InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
 {
   nsTArray<nsString> keys;
   aInit.GetKeys(keys);
   for (uint32_t i = 0; i < keys.Length() && !aRv.Failed(); ++i) {
     Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
   }
 }
+
+bool
+InternalHeaders::HasOnlySimpleHeaders() const
+{
+  for (uint32_t i = 0; i < mList.Length(); ++i) {
+    if (!IsSimpleHeader(mList[i].mName, mList[i].mValue)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// static
+already_AddRefed<InternalHeaders>
+InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)
+{
+  nsRefPtr<InternalHeaders> basic = new InternalHeaders(*aHeaders);
+  ErrorResult result;
+  // The Set-Cookie headers cannot be invalid mutable headers, so the Delete
+  // must succeed.
+  basic->Delete(NS_LITERAL_CSTRING("Set-Cookie"), result);
+  MOZ_ASSERT(!result.Failed());
+  basic->Delete(NS_LITERAL_CSTRING("Set-Cookie2"), result);
+  MOZ_ASSERT(!result.Failed());
+  return basic.forget();
+}
+
+// static
+already_AddRefed<InternalHeaders>
+InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
+{
+  nsRefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
+  ErrorResult result;
+
+  nsAutoTArray<nsCString, 1> acExposedNames;
+  aHeaders->GetAll(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
+  MOZ_ASSERT(!result.Failed());
+
+  nsCaseInsensitiveCStringArrayComparator comp;
+  for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
+    const Entry& entry = aHeaders->mList[i];
+    if (entry.mName.EqualsASCII("cache-control") ||
+        entry.mName.EqualsASCII("content-language") ||
+        entry.mName.EqualsASCII("content-type") ||
+        entry.mName.EqualsASCII("expires") ||
+        entry.mName.EqualsASCII("last-modified") ||
+        entry.mName.EqualsASCII("pragma") ||
+        acExposedNames.Contains(entry.mName, comp)) {
+      cors->Append(entry.mName, entry.mValue, result);
+      MOZ_ASSERT(!result.Failed());
+    }
+  }
+
+  return cors.forget();
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalHeaders.h
+++ b/dom/fetch/InternalHeaders.h
@@ -72,16 +72,24 @@ public:
   void Clear();
 
   HeadersGuardEnum Guard() const { return mGuard; }
   void SetGuard(HeadersGuardEnum aGuard, ErrorResult& aRv);
 
   void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
   void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
   void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
+
+  bool HasOnlySimpleHeaders() const;
+
+  static already_AddRefed<InternalHeaders>
+  BasicHeaders(InternalHeaders* aHeaders);
+
+  static already_AddRefed<InternalHeaders>
+  CORSHeaders(InternalHeaders* aHeaders);
 private:
   virtual ~InternalHeaders();
 
   static bool IsSimpleHeader(const nsACString& aName,
                              const nsACString& aValue);
   static bool IsInvalidName(const nsACString& aName, ErrorResult& aRv);
   static bool IsInvalidValue(const nsACString& aValue, ErrorResult& aRv);
   bool IsImmutable(ErrorResult& aRv) const;
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -61,17 +61,21 @@ public:
     , mMode(RequestMode::No_cors)
     , mCredentialsMode(RequestCredentials::Omit)
     , mResponseTainting(RESPONSETAINT_BASIC)
     , mRedirectCount(0)
     , mAuthenticationFlag(false)
     , mForceOriginHeader(false)
     , mManualRedirect(false)
     , mPreserveContentCodings(false)
-    , mSameOriginDataURL(false)
+      // FIXME(nsm): This should be false by default, but will lead to the
+      // algorithm never loading data: URLs right now. See Bug 1018872 about
+      // how certain contexts will override it to set it to true. Fetch
+      // specification does not handle this yet.
+    , mSameOriginDataURL(true)
     , mSkipServiceWorker(false)
     , mSynchronous(false)
     , mUnsafeRequest(false)
     , mUseURLCredentials(false)
   {
   }
 
   explicit InternalRequest(const InternalRequest& aOther)
@@ -108,16 +112,24 @@ public:
   }
 
   void
   SetMethod(const nsACString& aMethod)
   {
     mMethod.Assign(aMethod);
   }
 
+  bool
+  HasSimpleMethod() const
+  {
+    return mMethod.LowerCaseEqualsASCII("get") ||
+           mMethod.LowerCaseEqualsASCII("post") ||
+           mMethod.LowerCaseEqualsASCII("head");
+  }
+
   void
   GetURL(nsCString& aURL) const
   {
     aURL.Assign(mURL);
   }
 
   bool
   ReferrerIsNone() const
@@ -172,22 +184,40 @@ public:
   }
 
   void
   SetCredentialsMode(RequestCredentials aCredentialsMode)
   {
     mCredentialsMode = aCredentialsMode;
   }
 
+  ResponseTainting
+  GetResponseTainting() const
+  {
+    return mResponseTainting;
+  }
+
+  void
+  SetResponseTainting(ResponseTainting aTainting)
+  {
+    mResponseTainting = aTainting;
+  }
+
   nsContentPolicyType
   GetContext() const
   {
     return mContext;
   }
 
+  bool
+  UnsafeRequest() const
+  {
+    return mUnsafeRequest;
+  }
+
   InternalHeaders*
   Headers()
   {
     return mHeaders;
   }
 
   bool
   ForceOriginHeader()
@@ -196,16 +226,22 @@ public:
   }
 
   void
   GetOrigin(nsCString& aOrigin) const
   {
     aOrigin.Assign(mOrigin);
   }
 
+  bool
+  SameOriginDataURL() const
+  {
+    return mSameOriginDataURL;
+  }
+
   void
   SetBody(nsIInputStream* aStream)
   {
     mBodyStream = aStream;
   }
 
   // Will return the original stream!
   // Use a tee or copy if you don't want to erase the original.
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -15,10 +15,45 @@ namespace dom {
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
 {
 }
 
+// Headers are not copied since BasicResponse and CORSResponse both need custom
+// header handling.
+InternalResponse::InternalResponse(const InternalResponse& aOther)
+  : mType(aOther.mType)
+  , mTerminationReason(aOther.mTerminationReason)
+  , mURL(aOther.mURL)
+  , mStatus(aOther.mStatus)
+  , mStatusText(aOther.mStatusText)
+  , mBody(aOther.mBody)
+  , mContentType(aOther.mContentType)
+{
+}
+
+// static
+already_AddRefed<InternalResponse>
+InternalResponse::BasicResponse(InternalResponse* aInner)
+{
+  MOZ_ASSERT(aInner);
+  nsRefPtr<InternalResponse> basic = new InternalResponse(*aInner);
+  basic->mType = ResponseType::Basic;
+  basic->mHeaders = InternalHeaders::BasicHeaders(aInner->mHeaders);
+  return basic.forget();
+}
+
+// static
+already_AddRefed<InternalResponse>
+InternalResponse::CORSResponse(InternalResponse* aInner)
+{
+  MOZ_ASSERT(aInner);
+  nsRefPtr<InternalResponse> cors = new InternalResponse(*aInner);
+  cors->mType = ResponseType::Cors;
+  cors->mHeaders = InternalHeaders::CORSHeaders(aInner->mHeaders);
+  return cors.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -20,43 +20,65 @@ class InternalResponse MOZ_FINAL
 {
   friend class FetchDriver;
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InternalResponse)
 
   InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
 
-  explicit InternalResponse(const InternalResponse& aOther) MOZ_DELETE;
-
   static already_AddRefed<InternalResponse>
   NetworkError()
   {
     nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
     response->mType = ResponseType::Error;
     return response.forget();
   }
 
+  static already_AddRefed<InternalResponse>
+  OpaqueResponse()
+  {
+    nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
+    response->mType = ResponseType::Opaque;
+    return response.forget();
+  }
+
+  // DO NOT use the inner response after filtering it since the filtered
+  // response will adopt the inner response's body.
+  static already_AddRefed<InternalResponse>
+  BasicResponse(InternalResponse* aInner);
+
+  // DO NOT use the inner response after filtering it since the filtered
+  // response will adopt the inner response's body.
+  static already_AddRefed<InternalResponse>
+  CORSResponse(InternalResponse* aInner);
+
   ResponseType
   Type() const
   {
     return mType;
   }
 
   bool
   IsError() const
   {
     return Type() == ResponseType::Error;
   }
 
   // FIXME(nsm): Return with exclude fragment.
-  nsCString&
-  GetUrl()
+  void
+  GetUrl(nsCString& aURL) const
   {
-    return mURL;
+    aURL.Assign(mURL);
+  }
+
+  void
+  SetUrl(const nsACString& aURL)
+  {
+    mURL.Assign(aURL);
   }
 
   uint16_t
   GetStatus() const
   {
     return mStatus;
   }
 
@@ -84,16 +106,20 @@ public:
   {
     mBody = aBody;
   }
 
 private:
   ~InternalResponse()
   { }
 
+  // Used to create filtered responses.
+  // Does not copy headers.
+  explicit InternalResponse(const InternalResponse& aOther);
+
   ResponseType mType;
   nsCString mTerminationReason;
   nsCString mURL;
   const uint16_t mStatus;
   const nsCString mStatusText;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
   nsCString mContentType;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -166,22 +166,19 @@ Request::Constructor(const GlobalObject&
     headers = h->GetInternalHeaders();
   } else {
     headers = new InternalHeaders(*requestHeaders);
   }
 
   requestHeaders->Clear();
 
   if (request->Mode() == RequestMode::No_cors) {
-    nsCString method;
-    request->GetMethod(method);
-    ToLowerCase(method);
-    if (!method.EqualsASCII("get") &&
-        !method.EqualsASCII("head") &&
-        !method.EqualsASCII("post")) {
+    if (!request->HasSimpleMethod()) {
+      nsAutoCString method;
+      request->GetMethod(method);
       NS_ConvertUTF8toUTF16 label(method);
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
     requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
     if (aRv.Failed()) {
       return nullptr;
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -46,17 +46,19 @@ public:
   Type() const
   {
     return mInternalResponse->Type();
   }
 
   void
   GetUrl(DOMString& aUrl) const
   {
-    aUrl.AsAString() = NS_ConvertUTF8toUTF16(mInternalResponse->GetUrl());
+    nsCString url;
+    mInternalResponse->GetUrl(url);
+    aUrl.AsAString() = NS_ConvertUTF8toUTF16(url);
   }
 
   uint16_t
   Status() const
   {
     return mInternalResponse->GetStatus();
   }
 
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -1,33 +1,37 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.dom += [
     'Fetch.h',
+    'FetchDriver.h',
     'Headers.h',
     'InternalHeaders.h',
     'InternalRequest.h',
     'InternalResponse.h',
     'Request.h',
     'Response.h',
 ]
 
 UNIFIED_SOURCES += [
     'Fetch.cpp',
+    'FetchDriver.cpp',
     'Headers.cpp',
     'InternalHeaders.cpp',
     'InternalRequest.cpp',
     'InternalResponse.cpp',
     'Request.cpp',
     'Response.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../workers',
+    # For nsDataHandler.h
+    '/netwerk/protocol/data',
 ]
 
 FAIL_ON_WARNINGS = True
 MSVC_ENABLE_PGO = True
 FINAL_LIBRARY = 'xul'
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -9,19 +9,22 @@ const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
 Cu.import("resource://gre/modules/PermissionsTable.jsm");
 Cu.import("resource://gre/modules/PermissionSettings.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
+                                   "@mozilla.org/datastore-service;1",
+                                   "nsIDataStoreService");
+
 this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
-                         "SystemMessagePermissionsTable",
-                         "SystemMessagePrefixPermissionsTable"];
+                         "SystemMessagePermissionsTable"];
 
 function debug(aStr) {
   // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
 }
 
 // This table maps system message to permission(s), indicating only
 // the system messages granted by the page's permissions are allowed
 // to be registered or sent to that page. Note the empty permission
@@ -124,27 +127,16 @@ this.SystemMessagePermissionsTable = {
     "settings": ["read", "write"]
   },
   "wifip2p-pairing-request": { },
   "first-run-with-sim": {
     "settings": ["read", "write"]
   }
 };
 
-// This table maps system message prefix to permission(s), indicating only
-// the system messages with specified prefixes granted by the page's permissions
-// are allowed to be registered or sent to that page. Note the empty permission
-// set means this type of system message is always permitted.
-//
-// Note that this table is only used when the permission checker can't find a
-// match in SystemMessagePermissionsTable listed above.
-
-this.SystemMessagePrefixPermissionsTable = {
-  "datastore-update-": { },
-};
 
 this.SystemMessagePermissionsChecker = {
   /**
    * Return all the needed permission names for the given system message.
    * @param string aSysMsgName
    *        The system messsage name.
    * @returns object
    *        Format: { permName (string): permNamesWithAccess (string array), ... }
@@ -154,29 +146,19 @@ this.SystemMessagePermissionsChecker = {
    *        Return and report error when any unexpected error is ecountered.
    *        Ex, when the system message we want to search is not included.
    **/
   getSystemMessagePermissions: function getSystemMessagePermissions(aSysMsgName) {
     debug("getSystemMessagePermissions(): aSysMsgName: " + aSysMsgName);
 
     let permNames = SystemMessagePermissionsTable[aSysMsgName];
     if (permNames === undefined) {
-      // Try to look up in the prefix table.
-      for (let sysMsgPrefix in SystemMessagePrefixPermissionsTable) {
-        if (aSysMsgName.indexOf(sysMsgPrefix) === 0) {
-          permNames = SystemMessagePrefixPermissionsTable[sysMsgPrefix];
-          break;
-        }
-      }
-
-      if (permNames === undefined) {
-        debug("'" + aSysMsgName + "' is not associated with permissions. " +
-              "Please add them to the SystemMessage[Prefix]PermissionsTable.");
-        return null;
-      }
+      debug("'" + aSysMsgName + "' is not associated with permissions. " +
+            "Please add them to the SystemMessage[Prefix]PermissionsTable.");
+      return null;
     }
 
     let object = { };
     for (let permName in permNames) {
       if (PermissionsTable[permName] === undefined) {
         debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " +
               "Please correct it in the SystemMessage[Prefix]PermissionsTable.");
         return null;
@@ -190,16 +172,44 @@ this.SystemMessagePermissionsChecker = {
         return null;
       }
       object[permName] = appendAccessToPermName(permName, access);
     }
     return object
   },
 
   /**
+   * Check if the message is a datastore-update message
+   * @param string aSysMsgName
+   *        The system messsage name.
+   */
+  isDataStoreSystemMessage: function(aSysMsgName) {
+    return aSysMsgName.indexOf('datastore-update-') === 0;
+  },
+
+  /**
+   * Check if this manifest can deliver this particular datastore message.
+   */
+  canDeliverDataStoreSystemMessage: function(aSysMsgName, aManifestURL) {
+    let store = aSysMsgName.substr('datastore-update-'.length);
+
+    // Get all the manifest URLs of the apps which can access the datastore.
+    let manifestURLs = dataStoreService.getAppManifestURLsForDataStore(store);
+    let enumerate = manifestURLs.enumerate();
+    while (enumerate.hasMoreElements()) {
+      let manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
+      if (manifestURL == aManifestURL) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  /**
    * Check if the system message is permitted to be registered for the given
    * app at start-up based on the permissions claimed in the app's manifest.
    * @param string aSysMsgName
    *        The system messsage name.
    * @param string aManifestURL
    *        The app's manifest URL.
    * @param object aManifest
    *        The app's manifest.
@@ -210,16 +220,21 @@ this.SystemMessagePermissionsChecker = {
     function isSystemMessagePermittedToRegister(aSysMsgName,
                                                 aManifestURL,
                                                 aManifest) {
     debug("isSystemMessagePermittedToRegister(): " +
           "aSysMsgName: " + aSysMsgName + ", " +
           "aManifestURL: " + aManifestURL + ", " +
           "aManifest: " + JSON.stringify(aManifest));
 
+    if (this.isDataStoreSystemMessage(aSysMsgName) &&
+        this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
+      return true;
+    }
+
     let permNames = this.getSystemMessagePermissions(aSysMsgName);
     if (permNames === null) {
       return false;
     }
 
     // Check to see if the 'webapp' is app/privileged/certified.
     let appStatus;
     switch (AppsUtils.getAppManifestStatus(aManifest)) {
@@ -297,16 +312,21 @@ this.SystemMessagePermissionsChecker = {
    **/
   isSystemMessagePermittedToSend:
     function isSystemMessagePermittedToSend(aSysMsgName, aPageURL, aManifestURL) {
     debug("isSystemMessagePermittedToSend(): " +
           "aSysMsgName: " + aSysMsgName + ", " +
           "aPageURL: " + aPageURL + ", " +
           "aManifestURL: " + aManifestURL);
 
+    if (this.isDataStoreSystemMessage(aSysMsgName) &&
+        this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
+      return true;
+    }
+
     let permNames = this.getSystemMessagePermissions(aSysMsgName);
     if (permNames === null) {
       return false;
     }
 
     let pageURI = Services.io.newURI(aPageURL, null, null);
     for (let permName in permNames) {
       let permNamesWithAccess = permNames[permName];
--- a/dom/plugins/base/nsNPAPIPlugin.h
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -121,17 +121,17 @@ inline NPIdentifier
 IntToNPIdentifier(int i)
 {
     return JSIdToNPIdentifier(INT_TO_JSID(i));
 }
 
 JSContext* GetJSContext(NPP npp);
 
 inline bool
-NPStringIdentifierIsPermanent(NPP npp, NPIdentifier id)
+NPStringIdentifierIsPermanent(NPIdentifier id)
 {
   AutoSafeJSContext cx;
   return JS_StringHasBeenInterned(cx, NPIdentifierToString(id));
 }
 
 #define NPIdentifier_VOID (JSIdToNPIdentifier(JSID_VOID))
 
 NPObject*
deleted file mode 100644
--- a/dom/plugins/ipc/PPluginIdentifier.ipdl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-include protocol PPluginModule;
-
-namespace mozilla {
-namespace plugins {
-
-/**
- * Represents an NPIdentifier that wraps either a string or an integer.
- */
-async protocol PPluginIdentifier
-{
-  manager PPluginModule;
-
-parent:
-  /**
-   * If this is a temporary identifier, inform the parent that the plugin
-   * has made the identifier permanent by calling NPN_GetStringIdentifier.
-   */
-  async Retain();
-
-child:
-  async __delete__();
-};
-
-} // namespace plugins
-} // namespace mozilla
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -1,50 +1,32 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-include protocol PPluginIdentifier;
 include protocol PPluginInstance;
 include protocol PPluginScriptableObject;
 include protocol PCrashReporter;
 
-
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace plugins {
 
 intr protocol PPluginModule
 {
   manages PPluginInstance;
-  manages PPluginIdentifier;
   manages PCrashReporter;
 
 both:
-  /**
-   * Sending a void string to this constructor creates an int identifier whereas
-   * sending a non-void string will create a string identifier. This constructor
-   * may be called by either child or parent. If a race occurs by calling the
-   * constructor with the same string or int argument then we create two actors
-   * and detect the second instance in the child. We prevent the parent's actor
-   * from leaking out to plugin code and only allow the child's to be used.
-   *
-   * When calling into the plugin, the parent may create a "temporary"
-   * identifier which is only valid for the lifetime of the current inerrupt frame.
-   */
-  async PPluginIdentifier(nsCString aString,
-                          int32_t aInt,
-                          bool temporary);
-
   // Window-specific message which instructs the interrupt mechanism to enter
   // a nested event loop for the current interrupt call.
   async ProcessNativeEventsInInterruptCall();
 
 child:
   // Forces the child process to update its plugin function table.
   intr NP_GetEntryPoints()
     returns (NPError rv);
--- a/dom/plugins/ipc/PPluginScriptableObject.ipdl
+++ b/dom/plugins/ipc/PPluginScriptableObject.ipdl
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PPluginInstance;
-include protocol PPluginIdentifier;
+include PluginTypes;
 
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace plugins {
 
 union Variant {
@@ -34,40 +34,40 @@ parent:
     returns (Variant aResult,
              bool aSuccess);
 
 child:
   intr Invalidate();
 
 both:
   // NPClass methods
-  intr HasMethod(PPluginIdentifier aId)
+  intr HasMethod(PluginIdentifier aId)
     returns (bool aHasMethod);
 
-  intr Invoke(PPluginIdentifier aId,
+  intr Invoke(PluginIdentifier aId,
              Variant[] aArgs)
     returns (Variant aResult,
              bool aSuccess);
 
   intr InvokeDefault(Variant[] aArgs)
     returns (Variant aResult,
              bool aSuccess);
 
-  intr HasProperty(PPluginIdentifier aId)
+  intr HasProperty(PluginIdentifier aId)
     returns (bool aHasProperty);
 
-  intr SetProperty(PPluginIdentifier aId,
+  intr SetProperty(PluginIdentifier aId,
                   Variant aValue)
     returns (bool aSuccess);
 
-  intr RemoveProperty(PPluginIdentifier aId)
+  intr RemoveProperty(PluginIdentifier aId)
     returns (bool aSuccess);
 
   intr Enumerate()
-    returns (PPluginIdentifier[] aProperties,
+    returns (PluginIdentifier[] aProperties,
              bool aSuccess);
 
   intr Construct(Variant[] aArgs)
     returns (Variant aResult,
              bool aSuccess);
 
   // Objects are initially unprotected, and the Protect and Unprotect functions
   // only affect protocol objects that represent NPObjects created in the same
@@ -81,22 +81,22 @@ both:
 
   /**
    * GetProperty is slightly wonky due to the way we support NPObjects that have
    * methods and properties with the same name. When child calls parent we
    * simply return a property. When parent calls child, however, we need to do
    * several checks at once and return all the results simultaneously.
    */
 parent:
-  intr GetParentProperty(PPluginIdentifier aId)
+  intr GetParentProperty(PluginIdentifier aId)
     returns (Variant aResult,
              bool aSuccess);
 
 child:
-  intr GetChildProperty(PPluginIdentifier aId)
+  intr GetChildProperty(PluginIdentifier aId)
     returns (bool aHasProperty,
              bool aHasMethod,
              Variant aResult,
              bool aSuccess);
 };
 
 } // namespace plugins
 } // namespace mozilla
deleted file mode 100644
--- a/dom/plugins/ipc/PluginIdentifierChild.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 et :
- * 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 "PluginIdentifierChild.h"
-#include "PluginModuleChild.h"
-
-namespace mozilla {
-namespace plugins {
-
-void
-PluginIdentifierChild::MakePermanent()
-{
-  if (mCanonicalIdentifier) {
-    NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
-                 "Canonical identifiers should always be permanent.");
-    return; // nothing to do
-  }
-
-  if (!mHashed) {
-    NS_ASSERTION(mTemporaryRefs == 0, "Not hashed, but temporary refs?");
-
-    PluginIdentifierChild* c = GetCanonical();
-    if (c) {
-      NS_ASSERTION(c != this, "How did I get in the hash?");
-      mCanonicalIdentifier = c;
-      NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
-                   "Canonical identifiers should always be permanent.");
-      return;
-    }
-
-    Hash();
-    mHashed = true;
-    return;
-  }
-
-  if (mTemporaryRefs) {
-    SendRetain();
-    mTemporaryRefs = 0;
-  }
-}
-
-void
-PluginIdentifierChild::StartTemporary()
-{
-  if (mCanonicalIdentifier) {
-    NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
-                 "Canonical identifiers should always be permanent.");
-    return; // nothing to do
-  }
-
-  if (!mHashed) {
-    NS_ASSERTION(mTemporaryRefs == 0, "Not hashed, but temporary refs?");
-
-    PluginIdentifierChild* c = GetCanonical();
-    if (c) {
-      NS_ASSERTION(c != this, "How did I get in the hash?");
-      mCanonicalIdentifier = c;
-      NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
-                   "Canonical identifiers should always be permanent.");
-      return;
-    }
-
-    Hash();
-    mHashed = true;
-    mTemporaryRefs = 1;
-    return;
-  }
-
-  if (mTemporaryRefs)
-    ++mTemporaryRefs;
-}
-
-void
-PluginIdentifierChild::FinishTemporary()
-{
-  if (mCanonicalIdentifier)
-    return;
-
-  NS_ASSERTION(mHashed, "Finishing unhashed identifier?");
-  if (!mTemporaryRefs)
-    return;
-
-  --mTemporaryRefs;
-  if (mTemporaryRefs)
-    return;
-
-  Unhash();
-  mHashed = false;
-}
-
-PluginIdentifierChild*
-PluginIdentifierChildString::GetCanonical()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  return module->mStringIdentifiers.Get(mString);
-}
-
-void
-PluginIdentifierChildString::Hash()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  NS_ASSERTION(module->mStringIdentifiers.Get(mString) == nullptr, "Replacing Hash?");
-  module->mStringIdentifiers.Put(mString, this);
-}
-
-void
-PluginIdentifierChildString::Unhash()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  NS_ASSERTION(module->mStringIdentifiers.Get(mString) == this, "Incorrect identifier hash?");
-  module->mStringIdentifiers.Remove(mString);
-}
-
-PluginIdentifierChild*
-PluginIdentifierChildInt::GetCanonical()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  return module->mIntIdentifiers.Get(mInt);
-}
-
-void
-PluginIdentifierChildInt::Hash()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  NS_ASSERTION(module->mIntIdentifiers.Get(mInt) == nullptr, "Replacing Hash?");
-  module->mIntIdentifiers.Put(mInt, this);
-}
-
-void
-PluginIdentifierChildInt::Unhash()
-{
-  PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
-  NS_ASSERTION(module->mIntIdentifiers.Get(mInt) == this, "Incorrect identifier hash?");
-  module->mIntIdentifiers.Remove(mInt);
-}
-
-} // namespace mozilla::plugins
-} // namespace mozilla
deleted file mode 100644
--- a/dom/plugins/ipc/PluginIdentifierChild.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 et :
- * 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 dom_plugins_PluginIdentifierChild_h
-#define dom_plugins_PluginIdentifierChild_h
-
-#include "mozilla/plugins/PPluginIdentifierChild.h"
-#include "npapi.h"
-#include "npruntime.h"
-
-#include "nsString.h"
-
-namespace mozilla {
-namespace plugins {
-
-class PluginModuleChild;
-
-/**
- * Plugin identifiers may be "temporary", see the comment on the
- * PPluginIdentifier constructor for details. This means that any IPDL method
- * which receives a PPluginIdentifierChild* parameter must use StackIdentifier
- * to track it.
- */
-class PluginIdentifierChild : public PPluginIdentifierChild
-{
-  friend class PluginModuleChild;
-public:
-  bool IsString()
-  {
-    return mIsString;
-  }
-
-  NPIdentifier ToNPIdentifier()
-  {
-    if (mCanonicalIdentifier) {
-      return mCanonicalIdentifier;
-    }
-
-    NS_ASSERTION(mHashed, "Handing out an unhashed identifier?");
-    return this;
-  }
-
-  void MakePermanent();
-
-  class MOZ_STACK_CLASS StackIdentifier
-  {
-  public:
-    explicit StackIdentifier(PPluginIdentifierChild* actor)
-      : mIdentifier(static_cast<PluginIdentifierChild*>(actor))
-    {
-      if (mIdentifier)
-        mIdentifier->StartTemporary();
-    }
-
-    ~StackIdentifier() {
-      if (mIdentifier)
-        mIdentifier->FinishTemporary();
-    }
-
-    PluginIdentifierChild* operator->() { return mIdentifier; }
-
-  private:
-    PluginIdentifierChild* mIdentifier;
-  };
-
-protected:
-  explicit PluginIdentifierChild(bool aIsString)
-    : mCanonicalIdentifier(nullptr)
-    , mHashed(false)
-    , mTemporaryRefs(0)
-    , mIsString(aIsString)
-  {
-    MOZ_COUNT_CTOR(PluginIdentifierChild);
-  }
-
-  virtual ~PluginIdentifierChild()
-  {
-    MOZ_COUNT_DTOR(PluginIdentifierChild);
-  }
-
-  // The following functions are implemented by the subclasses for their
-  // identifier maps.
-  virtual PluginIdentifierChild* GetCanonical() = 0;
-  virtual void Hash() = 0;
-  virtual void Unhash() = 0;
-
-private:
-  void StartTemporary();
-  void FinishTemporary();
-
-  // There's a possibility that we already have an actor that wraps the same
-  // string or int because we do all this identifier construction
-  // asynchronously. In this case we need to hand out the canonical version
-  // created by the child side.
-  //
-  // In order to deal with temporary identifiers which appear on the stack,
-  // identifiers use the following state invariants:
-  //
-  // * mCanonicalIdentifier is non-nullptr: this is a duplicate identifier, no
-  //   further information is necessary.
-  // * mHashed is false: this identifier is a newborn, non-permanent identifier
-  // * mHashed is true, mTemporaryRefs is 0: this identifier is permanent
-  // * mHashed is true, mTemporaryRefs is non-0: this identifier is temporary;
-  //   if NPN_GetFooIdentifier is called for it, we need to retain it. If
-  //   all stack references are lost, unhash it because it will soon be 
-  //   deleted.
-
-  PluginIdentifierChild* mCanonicalIdentifier;
-  bool mHashed;
-  unsigned int mTemporaryRefs;
-  bool mIsString;
-};
-
-class PluginIdentifierChildString : public PluginIdentifierChild
-{
-  friend class PluginModuleChild;
-public:
-  NPUTF8* ToString()
-  {
-    return ToNewCString(mString);
-  }
-
-protected:
-  explicit PluginIdentifierChildString(const nsCString& aString)
-    : PluginIdentifierChild(true),
-      mString(aString)
-  { }
-
-  virtual PluginIdentifierChild* GetCanonical();
-  virtual void Hash();
-  virtual void Unhash();
-
-  nsCString mString;
-};
-
-class PluginIdentifierChildInt : public PluginIdentifierChild
-{
-  friend class PluginModuleChild;
-public:
-  int32_t ToInt()
-  {
-    return mInt;
-  }
-
-protected:
-  explicit PluginIdentifierChildInt(int32_t aInt)
-    : PluginIdentifierChild(false),
-      mInt(aInt)
-  { }
-
-  virtual PluginIdentifierChild* GetCanonical();
-  virtual void Hash();
-  virtual void Unhash();
-
-  int32_t mInt;
-};
-
-} // namespace plugins
-} // namespace mozilla
-
-#endif // dom_plugins_PluginIdentifierChild_h
deleted file mode 100644
--- a/dom/plugins/ipc/PluginIdentifierParent.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 et :
- * 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 "PluginIdentifierParent.h"
-
-#include "nsNPAPIPlugin.h"
-#include "nsServiceManagerUtils.h"
-#include "PluginScriptableObjectUtils.h"
-#include "mozilla/unused.h"
-
-using namespace mozilla::plugins::parent;
-
-namespace mozilla {
-namespace plugins {
-
-void
-PluginIdentifierParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-  // Implement me! Bug 1005161
-}
-
-bool
-PluginIdentifierParent::RecvRetain()
-{
-  mTemporaryRefs = 0;
-
-  // Intern the jsid if necessary.
-  AutoSafeJSContext cx;
-  JS::Rooted<jsid> id(cx, NPIdentifierToJSId(mIdentifier));
-  if (!JSID_IS_STRING(id)) {
-    return true;
-  }
-
-  // The following is what nsNPAPIPlugin.cpp does. Gross, but the API doesn't
-  // give you a NPP to play with.
-  JS::Rooted<JSString*> str(cx, JSID_TO_STRING(id));
-  JSString* str2 = JS_InternJSString(cx, str);
-  if (!str2) {
-    return false;
-  }
-
-  NS_ASSERTION(str == str2, "Interning a string in a JSID should always return the same string.");
-
-  return true;
-}
-
-PluginIdentifierParent::StackIdentifier::StackIdentifier
-    (PluginInstanceParent* inst, NPIdentifier aIdentifier)
-  : mIdentifier(inst->Module()->GetIdentifierForNPIdentifier(inst->GetNPP(), aIdentifier))
-{
-}
-
-PluginIdentifierParent::StackIdentifier::StackIdentifier
-    (NPObject* aObject, NPIdentifier aIdentifier)
-  : mIdentifier(nullptr)
-{
-  PluginInstanceParent* inst = GetInstance(aObject);
-  mIdentifier = inst->Module()->GetIdentifierForNPIdentifier(inst->GetNPP(), aIdentifier);
-}
-
-PluginIdentifierParent::StackIdentifier::~StackIdentifier()
-{
-  if (!mIdentifier) {
-    return;
-  }
-
-  if (!mIdentifier->IsTemporary()) {
-    return;
-  }
-
-  if (mIdentifier->RemoveTemporaryRef()) {
-    unused << PPluginIdentifierParent::Send__delete__(mIdentifier);
-  }
-}
-
-} // namespace mozilla::plugins
-} // namespace mozilla
deleted file mode 100644
--- a/dom/plugins/ipc/PluginIdentifierParent.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 et :
- * 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 dom_plugins_PluginIdentifierParent_h
-#define dom_plugins_PluginIdentifierParent_h
-
-#include "mozilla/plugins/PPluginIdentifierParent.h"
-
-#include "npapi.h"
-#include "npruntime.h"
-
-namespace mozilla {
-namespace plugins {
-
-class PluginInstanceParent;
-
-class PluginIdentifierParent : public PPluginIdentifierParent
-{
-  friend class PluginModuleParent;
-
-public:
-  NPIdentifier ToNPIdentifier()
-  {
-    return mIdentifier;
-  }
-
-  bool IsTemporary() {
-    return !!mTemporaryRefs;
-  }
-
-  /**
-   * Holds a perhaps-temporary identifier for the current stack frame.
-   */
-  class MOZ_STACK_CLASS StackIdentifier
-  {
-  public:
-    StackIdentifier(PluginInstanceParent* inst, NPIdentifier aIdentifier);
-    StackIdentifier(NPObject* aObject, NPIdentifier aIdentifier);
-    ~StackIdentifier();
-
-    operator PluginIdentifierParent*() {
-      return mIdentifier;
-    }
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
-
-    PluginIdentifierParent* mIdentifier;
-  };
-
-protected:
-  PluginIdentifierParent(NPIdentifier aIdentifier, bool aTemporary)
-    : mIdentifier(aIdentifier)
-    , mTemporaryRefs(aTemporary ? 1 : 0)
-  {
-    MOZ_COUNT_CTOR(PluginIdentifierParent);
-  }
-
-  virtual ~PluginIdentifierParent()
-  {
-    MOZ_COUNT_DTOR(PluginIdentifierParent);
-  }
-
-  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
-
-  virtual bool RecvRetain() MOZ_OVERRIDE;
-
-  void AddTemporaryRef() {
-    mTemporaryRefs++;
-  }
-
-  /**
-   * @returns true if the last temporary reference was removed.
-   */
-  bool RemoveTemporaryRef() {
-    --mTemporaryRefs;
-    return !mTemporaryRefs;
-  }
-
-private:
-  NPIdentifier mIdentifier;
-  unsigned int mTemporaryRefs;
-};
-
-} // namespace plugins
-} // namespace mozilla
-
-#endif // dom_plugins_PluginIdentifierParent_h
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -32,17 +32,16 @@
 
 #ifdef MOZ_X11
 # include "mozilla/X11Util.h"
 #endif
 #include "mozilla/plugins/PluginInstanceChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
-#include "PluginIdentifierChild.h"
 #include "mozilla/dom/CrashReporterChild.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "COMMessageFilter.h"
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
@@ -116,16 +115,17 @@ PluginModuleChild::PluginModuleChild()
 PluginModuleChild::~PluginModuleChild()
 {
     NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
 
     // We don't unload the plugin library in case it uses atexit handlers or
     // other similar hooks.
 
     DeinitGraphics();
+    PluginScriptableObjectChild::ClearIdentifiers();
 
     gInstance = nullptr;
 }
 
 // static
 PluginModuleChild*
 PluginModuleChild::current()
 {
@@ -1839,48 +1839,16 @@ PluginModuleChild::AnswerNP_Initialize(N
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     *_retval = mInitializeFunc(&sBrowserFuncs);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
-PPluginIdentifierChild*
-PluginModuleChild::AllocPPluginIdentifierChild(const nsCString& aString,
-                                               const int32_t& aInt,
-                                               const bool& aTemporary)
-{
-    // We cannot call SetPermanent within this function because Manager() isn't
-    // set up yet.
-    if (aString.IsVoid()) {
-        return new PluginIdentifierChildInt(aInt);
-    }
-    return new PluginIdentifierChildString(aString);
-}
-
-bool
-PluginModuleChild::RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor,
-                                                    const nsCString& aString,
-                                                    const int32_t& aInt,
-                                                    const bool& aTemporary)
-{
-    if (!aTemporary) {
-        static_cast<PluginIdentifierChild*>(actor)->MakePermanent();
-    }
-    return true;
-}
-
-bool
-PluginModuleChild::DeallocPPluginIdentifierChild(PPluginIdentifierChild* aActor)
-{
-    delete aActor;
-    return true;
-}
-
 #if defined(XP_WIN)
 BOOL WINAPI
 PMCGetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
 {
   if (!pwi)
       return FALSE;
 
   if (!sGetWindowInfoPtrStub) {
@@ -2175,110 +2143,89 @@ NPIdentifier
 PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
     if (!aName)
         return 0;
 
-    PluginModuleChild* self = PluginModuleChild::current();
     nsDependentCString name(aName);
-
-    PluginIdentifierChildString* ident = self->mStringIdentifiers.Get(name);
-    if (!ident) {
-        nsCString nameCopy(name);
-
-        ident = new PluginIdentifierChildString(nameCopy);
-        self->SendPPluginIdentifierConstructor(ident, nameCopy, -1, false);
-    }
-    ident->MakePermanent();
-    return ident;
+    PluginIdentifier ident(name);
+    PluginScriptableObjectChild::StackIdentifier stackID(ident);
+    stackID.MakePermanent();
+    return stackID.ToNPIdentifier();
 }
 
 void
 PluginModuleChild::NPN_GetStringIdentifiers(const NPUTF8** aNames,
                                             int32_t aNameCount,
                                             NPIdentifier* aIdentifiers)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
     if (!(aNames && aNameCount > 0 && aIdentifiers)) {
         NS_RUNTIMEABORT("Bad input! Headed for a crash!");
     }
 
-    PluginModuleChild* self = PluginModuleChild::current();
-
     for (int32_t index = 0; index < aNameCount; ++index) {
         if (!aNames[index]) {
             aIdentifiers[index] = 0;
             continue;
         }
         nsDependentCString name(aNames[index]);
-        PluginIdentifierChildString* ident = self->mStringIdentifiers.Get(name);
-        if (!ident) {
-            nsCString nameCopy(name);
-
-            ident = new PluginIdentifierChildString(nameCopy);
-            self->SendPPluginIdentifierConstructor(ident, nameCopy, -1, false);
-        }
-        ident->MakePermanent();
-        aIdentifiers[index] = ident;
+        PluginIdentifier ident(name);
+        PluginScriptableObjectChild::StackIdentifier stackID(ident);
+        stackID.MakePermanent();
+        aIdentifiers[index] = stackID.ToNPIdentifier();
     }
 }
 
 bool
 PluginModuleChild::NPN_IdentifierIsString(NPIdentifier aIdentifier)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
-    PluginIdentifierChild* ident =
-        static_cast<PluginIdentifierChild*>(aIdentifier);
-    return ident->IsString();
+    PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
+    return stack.IsString();
 }
 
 NPIdentifier
 PluginModuleChild::NPN_GetIntIdentifier(int32_t aIntId)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
-    PluginModuleChild* self = PluginModuleChild::current();
-
-    PluginIdentifierChildInt* ident = self->mIntIdentifiers.Get(aIntId);
-    if (!ident) {
-        nsCString voidString;
-        voidString.SetIsVoid(true);
-
-        ident = new PluginIdentifierChildInt(aIntId);
-        self->SendPPluginIdentifierConstructor(ident, voidString, aIntId, false);
-    }
-    ident->MakePermanent();
-    return ident;
+    PluginIdentifier ident(aIntId);
+    PluginScriptableObjectChild::StackIdentifier stackID(ident);
+    stackID.MakePermanent();
+    return stackID.ToNPIdentifier();
 }
 
 NPUTF8*
 PluginModuleChild::NPN_UTF8FromIdentifier(NPIdentifier aIdentifier)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
-    if (static_cast<PluginIdentifierChild*>(aIdentifier)->IsString()) {
-      return static_cast<PluginIdentifierChildString*>(aIdentifier)->ToString();
+    PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
+    if (stackID.IsString()) {
+        return ToNewCString(stackID.GetString());
     }
     return nullptr;
 }
 
 int32_t
 PluginModuleChild::NPN_IntFromIdentifier(NPIdentifier aIdentifier)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
-    if (!static_cast<PluginIdentifierChild*>(aIdentifier)->IsString()) {
-      return static_cast<PluginIdentifierChildInt*>(aIdentifier)->ToInt();
+    PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
+    if (!stackID.IsString()) {
+        return stackID.GetInt();
     }
     return INT32_MIN;
 }
 
 #ifdef OS_WIN
 void
 PluginModuleChild::EnteredCall()
 {
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -25,17 +25,16 @@
 #include "nsHashKeys.h"
 
 #ifdef MOZ_WIDGET_COCOA
 #include "PluginInterposeOSX.h"
 #endif
 
 #include "mozilla/plugins/PPluginModuleChild.h"
 #include "mozilla/plugins/PluginInstanceChild.h"
-#include "mozilla/plugins/PluginIdentifierChild.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 
 // NOTE: stolen from nsNPAPIPlugin.h
 
 #if defined(XP_WIN)
 #define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name)
 #else
 #define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name)
@@ -72,30 +71,16 @@ protected:
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
     virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
     virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
 
-    virtual PPluginIdentifierChild*
-    AllocPPluginIdentifierChild(const nsCString& aString,
-                                const int32_t& aInt,
-                                const bool& aTemporary) MOZ_OVERRIDE;
-
-    virtual bool
-    RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor,
-                                     const nsCString& aString,
-                                     const int32_t& aInt,
-                                     const bool& aTemporary) MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPPluginIdentifierChild(PPluginIdentifierChild* aActor) MOZ_OVERRIDE;
-
     virtual PPluginInstanceChild*
     AllocPPluginInstanceChild(const nsCString& aMimeType,
                               const uint16_t& aMode,
                               const InfallibleTArray<nsCString>& aNames,
                               const InfallibleTArray<nsCString>& aValues,
                               NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
@@ -383,22 +368,16 @@ private:
         PluginScriptableObjectChild* actor;
     };
     /**
      * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
      * final release/dealloc, whether or not an actor is currently associated with the object.
      */
     nsTHashtable<NPObjectData> mObjectMap;
 
-    friend class PluginIdentifierChild;
-    friend class PluginIdentifierChildString;
-    friend class PluginIdentifierChildInt;
-    nsDataHashtable<nsCStringHashKey, PluginIdentifierChildString*> mStringIdentifiers;
-    nsDataHashtable<nsUint32HashKey, PluginIdentifierChildInt*> mIntIdentifiers;
-
 public: // called by PluginInstanceChild
     /**
      * Dealloc an NPObject after last-release or when the associated instance
      * is destroyed. This function will remove the object from mObjectMap.
      */
     static void DeallocNPObject(NPObject* o);
 
     NPError NPP_Destroy(PluginInstanceChild* instance) {
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -23,17 +23,16 @@
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
 #include "nsNPAPIPlugin.h"
 #include "nsPrintfCString.h"
-#include "PluginIdentifierParent.h"
 #include "prsystem.h"
 #include "GeckoProfiler.h"
 
 #ifdef XP_WIN
 #include "PluginHangUIParent.h"
 #include "mozilla/widget/AudioSession.h"
 #endif
 
@@ -774,47 +773,16 @@ PluginModuleParent::NotifyPluginCrashed(
                 &PluginModuleParent::NotifyPluginCrashed), 10);
         return;
     }
 
     if (mPlugin)
         mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
 }
 
-PPluginIdentifierParent*
-PluginModuleParent::AllocPPluginIdentifierParent(const nsCString& aString,
-                                                 const int32_t& aInt,
-                                                 const bool& aTemporary)
-{
-    if (aTemporary) {
-        NS_ERROR("Plugins don't create temporary identifiers.");
-        return nullptr; // should abort the plugin
-    }
-
-    NPIdentifier npident = aString.IsVoid() ?
-        mozilla::plugins::parent::_getintidentifier(aInt) :
-        mozilla::plugins::parent::_getstringidentifier(aString.get());
-
-    if (!npident) {
-        NS_WARNING("Failed to get identifier!");
-        return nullptr;
-    }
-
-    PluginIdentifierParent* ident = new PluginIdentifierParent(npident, false);
-    mIdentifiers.Put(npident, ident);
-    return ident;
-}
-
-bool
-PluginModuleParent::DeallocPPluginIdentifierParent(PPluginIdentifierParent* aActor)
-{
-    delete aActor;
-    return true;
-}
-
 PPluginInstanceParent*
 PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType,
                                                const uint16_t& aMode,
                                                const InfallibleTArray<nsCString>& aNames,
                                                const InfallibleTArray<nsCString>& aValues,
                                                NPError* rv)
 {
     NS_ERROR("Not reachable!");
@@ -1046,54 +1014,16 @@ PluginModuleParent::NPP_URLRedirectNotif
 
 bool
 PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent)
 {
     *userAgent = NullableString(mNPNIface->uagent(nullptr));
     return true;
 }
 
-PluginIdentifierParent*
-PluginModuleParent::GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier)
-{
-    PluginIdentifierParent* ident;
-    if (mIdentifiers.Get(aIdentifier, &ident)) {
-        if (ident->IsTemporary()) {
-            ident->AddTemporaryRef();
-        }
-        return ident;
-    }
-
-    nsCString string;
-    int32_t intval = -1;
-    bool temporary = false;
-    if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
-        NPUTF8* chars =
-            mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
-        if (!chars) {
-            return nullptr;
-        }
-        string.Adopt(chars);
-        temporary = !NPStringIdentifierIsPermanent(npp, aIdentifier);
-    }
-    else {
-        intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
-        string.SetIsVoid(true);
-    }
-
-    ident = new PluginIdentifierParent(aIdentifier, temporary);
-    if (!SendPPluginIdentifierConstructor(ident, string, intval, temporary))
-        return nullptr;
-
-    if (!temporary) {
-        mIdentifiers.Put(aIdentifier, ident);
-    }
-    return ident;
-}
-
 PluginInstanceParent*
 PluginModuleParent::InstCast(NPP instance)
 {
     PluginInstanceParent* ip =
         static_cast<PluginInstanceParent*>(instance->pdata);
 
     // If the plugin crashed and the PluginInstanceParent was deleted,
     // instance->pdata will be nullptr.
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -9,16 +9,17 @@
 
 #include "base/process.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/PluginLibrary.h"
 #include "mozilla/plugins/ScopedMethodFactory.h"
 #include "mozilla/plugins/PluginProcessParent.h"
 #include "mozilla/plugins/PPluginModuleParent.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/plugins/PluginTypes.h"
 #include "npapi.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 
 #ifdef MOZ_CRASHREPORTER
@@ -30,17 +31,16 @@ namespace dom {
 class PCrashReporterParent;
 class CrashReporterParent;
 }
 
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class BrowserStreamParent;
-class PluginIdentifierParent;
 class PluginInstanceParent;
 
 #ifdef XP_WIN
 class PluginHangUIParent;
 #endif
 
 /**
  * PluginModuleParent
@@ -62,24 +62,16 @@ class PluginModuleParent
 {
 private:
     typedef mozilla::PluginLibrary PluginLibrary;
     typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
     typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
 protected:
 
-    virtual PPluginIdentifierParent*
-    AllocPPluginIdentifierParent(const nsCString& aString,
-                                 const int32_t& aInt,
-                                 const bool& aTemporary) MOZ_OVERRIDE;
-
-    virtual bool
-    DeallocPPluginIdentifierParent(PPluginIdentifierParent* aActor) MOZ_OVERRIDE;
-
     PPluginInstanceParent*
     AllocPPluginInstanceParent(const nsCString& aMimeType,
                                const uint16_t& aMode,
                                const InfallibleTArray<nsCString>& aNames,
                                const InfallibleTArray<nsCString>& aValues,
                                NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
@@ -111,25 +103,16 @@ public:
 
     PluginProcessParent* Process() const { return mSubprocess; }
     base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
 
     bool OkToCleanup() const {
         return !IsOnCxxStack();
     }
 
-    /**
-     * Get an identifier actor for this NPIdentifier. If this is a temporary
-     * identifier, the temporary refcount is increased by one. This method
-     * is intended only for use by StackIdentifier and the scriptable
-     * Enumerate hook.
-     */
-    PluginIdentifierParent*
-    GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier);
-
     void ProcessRemoteNativeEventsInInterruptCall();
 
     void TerminateChildProcess(MessageLoop* aMsgLoop);
 
 #ifdef XP_WIN
     void
     ExitedCxxStack() MOZ_OVERRIDE;
 #endif // XP_WIN
@@ -293,17 +276,16 @@ private:
     void ShutdownPluginProfiling();
 #endif
 
     PluginProcessParent* mSubprocess;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
     const NPNetscapeFuncs* mNPNIface;
-    nsDataHashtable<nsPtrHashKey<void>, PluginIdentifierParent*> mIdentifiers;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     nsRefPtr<nsIObserver> mProfilerObserver;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -1,20 +1,135 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=2 et :
  * 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 "PluginScriptableObjectChild.h"
 #include "PluginScriptableObjectUtils.h"
-#include "PluginIdentifierChild.h"
+#include "mozilla/plugins/PluginTypes.h"
 
 using namespace mozilla::plugins;
 
+/**
+ * NPIdentifiers in the plugin process use a tagged representation. The low bit
+ * stores the tag. If it's zero, the identifier is a string, and the value is a
+ * pointer to a StoredIdentifier. If the tag bit is 1, then the rest of the
+ * NPIdentifier value is the integer itself. Like the JSAPI, we require that all
+ * integers stored in NPIdentifier be non-negative.
+ *
+ * String identifiers are stored in the sIdentifiers hashtable to ensure
+ * uniqueness. The lifetime of these identifiers is only as long as the incoming
+ * IPC call from the chrome process. If the plugin wants to retain an
+ * identifier, it needs to call NPN_GetStringIdentifier, which causes the
+ * mPermanent flag to be set on the identifier. When this flag is set, the
+ * identifier is saved until the plugin process exits.
+ *
+ * The StackIdentifier RAII class is used to manage ownership of
+ * identifiers. Any identifier obtained from this class should not be used
+ * outside its scope, except when the MakePermanent() method has been called on
+ * it.
+ *
+ * The lifetime of an NPIdentifier in the plugin process is totally divorced
+ * from the lifetime of an NPIdentifier in the chrome process (where an
+ * NPIdentifier is stored as a jsid). The JS GC in the chrome process is able to
+ * trace through the entire heap, unlike in the plugin process, so there is no
+ * reason to retain identifiers there.
+ */
+
+PluginScriptableObjectChild::IdentifierTable PluginScriptableObjectChild::sIdentifiers;
+
+/* static */ PluginScriptableObjectChild::StoredIdentifier*
+PluginScriptableObjectChild::HashIdentifier(const nsCString& aIdentifier)
+{
+  StoredIdentifier* stored = sIdentifiers.Get(aIdentifier);
+  if (stored) {
+    return stored;
+  }
+
+  stored = new StoredIdentifier(aIdentifier);
+  sIdentifiers.Put(aIdentifier, stored);
+  return stored;
+}
+
+/* static */ void
+PluginScriptableObjectChild::UnhashIdentifier(StoredIdentifier* aStored)
+{
+  MOZ_ASSERT(sIdentifiers.Get(aStored->mIdentifier));
+  sIdentifiers.Remove(aStored->mIdentifier);
+}
+
+/* static */ void
+PluginScriptableObjectChild::ClearIdentifiers()
+{
+  sIdentifiers.Clear();
+}
+
+PluginScriptableObjectChild::StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier)
+: mIdentifier(aIdentifier),
+  mStored(nullptr)
+{
+  if (aIdentifier.type() == PluginIdentifier::TnsCString) {
+    mStored = PluginScriptableObjectChild::HashIdentifier(mIdentifier.get_nsCString());
+  }
+}
+
+PluginScriptableObjectChild::StackIdentifier::StackIdentifier(NPIdentifier aIdentifier)
+: mStored(nullptr)
+{
+  uintptr_t bits = reinterpret_cast<uintptr_t>(aIdentifier);
+  if (bits & 1) {
+    int32_t num = int32_t(bits >> 1);
+    mIdentifier = PluginIdentifier(num);
+  } else {
+    mStored = static_cast<StoredIdentifier*>(aIdentifier);
+    mIdentifier = mStored->mIdentifier;
+  }
+}
+
+PluginScriptableObjectChild::StackIdentifier::~StackIdentifier()
+{
+  if (!mStored) {
+    return;
+  }
+
+  // Each StackIdentifier owns one reference to its StoredIdentifier. In
+  // addition, the sIdentifiers table owns a reference. If mPermanent is false
+  // and sIdentifiers has the last reference, then we want to remove the
+  // StoredIdentifier from the table (and destroy it).
+  StoredIdentifier *stored = mStored;
+  mStored = nullptr;
+  if (stored->mRefCnt == 1 && !stored->mPermanent) {
+    PluginScriptableObjectChild::UnhashIdentifier(stored);
+  }
+}
+
+NPIdentifier
+PluginScriptableObjectChild::StackIdentifier::ToNPIdentifier() const
+{
+  if (mStored) {
+    MOZ_ASSERT(mIdentifier.type() == PluginIdentifier::TnsCString);
+    MOZ_ASSERT((reinterpret_cast<uintptr_t>(mStored.get()) & 1) == 0);
+    return mStored;
+  }
+
+  int32_t num = mIdentifier.get_int32_t();
+  // The JS engine imposes this condition on int32s in jsids, so we assume it.
+  MOZ_ASSERT(num >= 0);
+  return reinterpret_cast<NPIdentifier>((num << 1) | 1);
+}
+
+static PluginIdentifier
+FromNPIdentifier(NPIdentifier aIdentifier)
+{
+  PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
+  return stack.GetIdentifier();
+}
+
 // static
 NPObject*
 PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance,
                                                 NPClass* aClass)
 {
   AssertPluginThread();
 
   if (aClass != GetClass()) {
@@ -80,17 +195,17 @@ PluginScriptableObjectChild::ScriptableH
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
-  actor->CallHasMethod(static_cast<PPluginIdentifierChild*>(aName), &result);
+  actor->CallHasMethod(FromNPIdentifier(aName), &result);
 
   return result;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject,
                                               NPIdentifier aName,
@@ -117,17 +232,17 @@ PluginScriptableObjectChild::ScriptableI
   ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
   if (!args.IsOk()) {
     NS_ERROR("Failed to convert arguments!");
     return false;
   }
 
   Variant remoteResult;
   bool success;
-  actor->CallInvoke(static_cast<PPluginIdentifierChild*>(aName), args,
+  actor->CallInvoke(FromNPIdentifier(aName), args,
                     &remoteResult, &success);
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(remoteResult, *aResult);
   return true;
@@ -191,17 +306,17 @@ PluginScriptableObjectChild::ScriptableH
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
-  actor->CallHasProperty(static_cast<PPluginIdentifierChild*>(aName), &result);
+  actor->CallHasProperty(FromNPIdentifier(aName), &result);
 
   return result;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
                                                    NPIdentifier aName,
@@ -220,17 +335,17 @@ PluginScriptableObjectChild::ScriptableG
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   Variant result;
   bool success;
-  actor->CallGetParentProperty(static_cast<PPluginIdentifierChild*>(aName),
+  actor->CallGetParentProperty(FromNPIdentifier(aName),
                                &result, &success);
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(result, *aResult);
   return true;
@@ -260,17 +375,17 @@ PluginScriptableObjectChild::ScriptableS
 
   ProtectedVariant value(*aValue, actor->GetInstance());
   if (!value.IsOk()) {
     NS_WARNING("Failed to convert variant!");
     return false;
   }
 
   bool success;
-  actor->CallSetProperty(static_cast<PPluginIdentifierChild*>(aName), value,
+  actor->CallSetProperty(FromNPIdentifier(aName), value,
                          &success);
 
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject,
@@ -288,17 +403,17 @@ PluginScriptableObjectChild::ScriptableR
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool success;
-  actor->CallRemoveProperty(static_cast<PPluginIdentifierChild*>(aName),
+  actor->CallRemoveProperty(FromNPIdentifier(aName),
                             &success);
 
   return success;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
@@ -316,17 +431,17 @@ PluginScriptableObjectChild::ScriptableE
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectChild> actor(object->parent);
   NS_ASSERTION(actor, "This shouldn't ever be null!");
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
-  AutoInfallibleTArray<PPluginIdentifierChild*, 10> identifiers;
+  AutoInfallibleTArray<PluginIdentifier, 10> identifiers;
   bool success;
   actor->CallEnumerate(&identifiers, &success);
 
   if (!success) {
     return false;
   }
 
   *aCount = identifiers.Length();
@@ -338,18 +453,20 @@ PluginScriptableObjectChild::ScriptableE
   *aIdentifiers = reinterpret_cast<NPIdentifier*>(
       PluginModuleChild::sBrowserFuncs.memalloc(*aCount * sizeof(NPIdentifier)));
   if (!*aIdentifiers) {
     NS_ERROR("Out of memory!");
     return false;
   }
 
   for (uint32_t index = 0; index < *aCount; index++) {
-    (*aIdentifiers)[index] =
-      static_cast<PPluginIdentifierChild*>(identifiers[index]);
+    StackIdentifier id(identifiers[index]);
+    // Make the id permanent in case the plugin retains it.
+    id.MakePermanent();
+    (*aIdentifiers)[index] = id.ToNPIdentifier();
   }
   return true;
 }
 
 // static
 bool
 PluginScriptableObjectChild::ScriptableConstruct(NPObject* aObject,
                                                  const NPVariant* aArgs,
@@ -605,17 +722,17 @@ PluginScriptableObjectChild::AnswerInval
   }
 
   Unprotect();
 
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerHasMethod(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
                                              bool* aHasMethod)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
     *aHasMethod = false;
     return true;
@@ -624,23 +741,23 @@ PluginScriptableObjectChild::AnswerHasMe
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
   NS_ASSERTION(mType == LocalObject, "Bad type!");
 
   if (!(mObject->_class && mObject->_class->hasMethod)) {
     *aHasMethod = false;
     return true;
   }
 
-  PluginIdentifierChild::StackIdentifier id(aId);
-  *aHasMethod = mObject->_class->hasMethod(mObject, id->ToNPIdentifier());
+  StackIdentifier id(aId);
+  *aHasMethod = mObject->_class->hasMethod(mObject, id.ToNPIdentifier());
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerInvoke(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
                                           const InfallibleTArray<Variant>& aArgs,
                                           Variant* aResult,
                                           bool* aSuccess)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerInvoke with an invalidated object!");
@@ -668,18 +785,18 @@ PluginScriptableObjectChild::AnswerInvok
   }
 
   for (uint32_t index = 0; index < argCount; index++) {
     ConvertToVariant(aArgs[index], convertedArgs[index]);
   }
 
   NPVariant result;
   VOID_TO_NPVARIANT(result);
-  PluginIdentifierChild::StackIdentifier id(aId);
-  bool success = mObject->_class->invoke(mObject, id->ToNPIdentifier(),
+  StackIdentifier id(aId);
+  bool success = mObject->_class->invoke(mObject, id.ToNPIdentifier(),
                                          convertedArgs.Elements(), argCount,
                                          &result);
 
   for (uint32_t index = 0; index < argCount; index++) {
     PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]);
   }
 
   if (!success) {
@@ -770,17 +887,17 @@ PluginScriptableObjectChild::AnswerInvok
   }
 
   *aResult = convertedResult;
   *aSuccess = true;
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerHasProperty(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
                                                bool* aHasProperty)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
     *aHasProperty = false;
     return true;
@@ -789,23 +906,23 @@ PluginScriptableObjectChild::AnswerHasPr
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
   NS_ASSERTION(mType == LocalObject, "Bad type!");
 
   if (!(mObject->_class && mObject->_class->hasProperty)) {
     *aHasProperty = false;
     return true;
   }
 
-  PluginIdentifierChild::StackIdentifier id(aId);
-  *aHasProperty = mObject->_class->hasProperty(mObject, id->ToNPIdentifier());
+  StackIdentifier id(aId);
+  *aHasProperty = mObject->_class->hasProperty(mObject, id.ToNPIdentifier());
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerGetChildProperty(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
                                                     bool* aHasProperty,
                                                     bool* aHasMethod,
                                                     Variant* aResult,
                                                     bool* aSuccess)
 {
   AssertPluginThread();
 
   *aHasProperty = *aHasMethod = *aSuccess = false;
@@ -819,18 +936,18 @@ PluginScriptableObjectChild::AnswerGetCh
   NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!");
   NS_ASSERTION(mType == LocalObject, "Bad type!");
 
   if (!(mObject->_class && mObject->_class->hasProperty &&
         mObject->_class->hasMethod && mObject->_class->getProperty)) {
     return true;
   }
 
-  PluginIdentifierChild::StackIdentifier stackID(aId);
-  NPIdentifier id = stackID->ToNPIdentifier();
+  StackIdentifier stackID(aId);
+  NPIdentifier id = stackID.ToNPIdentifier();
 
   *aHasProperty = mObject->_class->hasProperty(mObject, id);
   *aHasMethod = mObject->_class->hasMethod(mObject, id);
 
   if (*aHasProperty) {
     NPVariant result;
     VOID_TO_NPVARIANT(result);
 
@@ -845,17 +962,17 @@ PluginScriptableObjectChild::AnswerGetCh
       *aResult = converted;
     }
   }
 
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerSetProperty(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
                                                const Variant& aValue,
                                                bool* aSuccess)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
     *aSuccess = false;
@@ -866,35 +983,35 @@ PluginScriptableObjectChild::AnswerSetPr
   NS_ASSERTION(mType == LocalObject, "Bad type!");
 
   if (!(mObject->_class && mObject->_class->hasProperty &&
         mObject->_class->setProperty)) {
     *aSuccess = false;
     return true;
   }
 
-  PluginIdentifierChild::StackIdentifier stackID(aId);
-  NPIdentifier id = stackID->ToNPIdentifier();
+  StackIdentifier stackID(aId);
+  NPIdentifier id = stackID.ToNPIdentifier();
 
   if (!mObject->_class->hasProperty(mObject, id)) {
     *aSuccess = false;
     return true;
   }
 
   NPVariant converted;
   ConvertToVariant(aValue, converted);
 
   if ((*aSuccess = mObject->_class->setProperty(mObject, id, &converted))) {
     PluginModuleChild::sBrowserFuncs.releasevariantvalue(&converted);
   }
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerRemoveProperty(PPluginIdentifierChild* aId,
+PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
                                                   bool* aSuccess)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
     *aSuccess = false;
     return true;
@@ -904,27 +1021,27 @@ PluginScriptableObjectChild::AnswerRemov
   NS_ASSERTION(mType == LocalObject, "Bad type!");
 
   if (!(mObject->_class && mObject->_class->hasProperty &&
         mObject->_class->removeProperty)) {
     *aSuccess = false;
     return true;
   }
 
-  PluginIdentifierChild::StackIdentifier stackID(aId);
-  NPIdentifier id = stackID->ToNPIdentifier();
+  StackIdentifier stackID(aId);
+  NPIdentifier id = stackID.ToNPIdentifier();
   *aSuccess = mObject->_class->hasProperty(mObject, id) ?
               mObject->_class->removeProperty(mObject, id) :
               true;
 
   return true;
 }
 
 bool
-PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PPluginIdentifierChild*>* aProperties,
+PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
                                              bool* aSuccess)
 {
   AssertPluginThread();
 
   if (mInvalidated) {
     NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
     *aSuccess = false;
     return true;
@@ -943,18 +1060,17 @@ PluginScriptableObjectChild::AnswerEnume
   if (!mObject->_class->enumerate(mObject, &ids, &idCount)) {
     *aSuccess = false;
     return true;
   }
 
   aProperties->SetCapacity(idCount);
 
   for (uint32_t index = 0; index < idCount; index++) {
-    PluginIdentifierChild* id = static_cast<PluginIdentifierChild*>(ids[index]);
-    aProperties->AppendElement(id);
+    aProperties->AppendElement(FromNPIdentifier(ids[index]));
   }
 
   PluginModuleChild::sBrowserFuncs.memfree(ids);
   *aSuccess = true;
   return true;
 }
 
 bool
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -4,25 +4,26 @@
  * 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 dom_plugins_PluginScriptableObjectChild_h
 #define dom_plugins_PluginScriptableObjectChild_h 1
 
 #include "mozilla/plugins/PPluginScriptableObjectChild.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
+#include "mozilla/plugins/PluginTypes.h"
 
 #include "npruntime.h"
+#include "nsDataHashtable.h"
 
 namespace mozilla {
 namespace plugins {
 
 class PluginInstanceChild;
 class PluginScriptableObjectChild;
-class PPluginIdentifierChild;
 
 struct ChildNPObject : NPObject
 {
   ChildNPObject()
     : NPObject(), parent(nullptr), invalidated(false)
   {
     MOZ_COUNT_CTOR(ChildNPObject);
   }
@@ -52,52 +53,52 @@ public:
   void
   InitializeLocal(NPObject* aObject);
 
 
   virtual bool
   AnswerInvalidate() MOZ_OVERRIDE;
 
   virtual bool
-  AnswerHasMethod(PPluginIdentifierChild* aId,
+  AnswerHasMethod(const PluginIdentifier& aId,
                   bool* aHasMethod) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerInvoke(PPluginIdentifierChild* aId,
+  AnswerInvoke(const PluginIdentifier& aId,
                const InfallibleTArray<Variant>& aArgs,
                Variant* aResult,
                bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
   AnswerInvokeDefault(const InfallibleTArray<Variant>& aArgs,
                       Variant* aResult,
                       bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerHasProperty(PPluginIdentifierChild* aId,
+  AnswerHasProperty(const PluginIdentifier& aId,
                     bool* aHasProperty) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerGetChildProperty(PPluginIdentifierChild* aId,
+  AnswerGetChildProperty(const PluginIdentifier& aId,
                          bool* aHasProperty,
                          bool* aHasMethod,
                          Variant* aResult,
                          bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerSetProperty(PPluginIdentifierChild* aId,
+  AnswerSetProperty(const PluginIdentifier& aId,
                     const Variant& aValue,
                     bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerRemoveProperty(PPluginIdentifierChild* aId,
+  AnswerRemoveProperty(const PluginIdentifier& aId,
                        bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerEnumerate(InfallibleTArray<PPluginIdentifierChild*>* aProperties,
+  AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
                   bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
   AnswerConstruct(const InfallibleTArray<Variant>& aArgs,
                   Variant* aResult,
                   bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
@@ -152,16 +153,76 @@ public:
            NPVariant* aResult);
 
   ScriptableObjectType
   Type() const {
     return mType;
   }
 
 private:
+  struct StoredIdentifier
+  {
+    nsCString mIdentifier;
+    nsAutoRefCnt mRefCnt;
+    bool mPermanent;
+
+    nsrefcnt AddRef() {
+      ++mRefCnt;
+      return mRefCnt;
+    }
+
+    nsrefcnt Release() {
+      --mRefCnt;
+      if (mRefCnt == 0) {
+        delete this;
+        return 0;
+      }
+      return mRefCnt;
+    }
+
+    explicit StoredIdentifier(const nsCString& aIdentifier)
+      : mIdentifier(aIdentifier), mRefCnt(), mPermanent(false)
+    { MOZ_COUNT_CTOR(StoredIdentifier); }
+
+    ~StoredIdentifier() { MOZ_COUNT_DTOR(StoredIdentifier); }
+  };
+
+public:
+  class MOZ_STACK_CLASS StackIdentifier
+  {
+  public:
+    StackIdentifier(const PluginIdentifier& aIdentifier);
+    StackIdentifier(NPIdentifier aIdentifier);
+    ~StackIdentifier();
+
+    void MakePermanent()
+    {
+      if (mStored) {
+        mStored->mPermanent = true;
+      }
+    }
+    NPIdentifier ToNPIdentifier() const;
+
+    bool IsString() const { return mIdentifier.type() == PluginIdentifier::TnsCString; }
+    const nsCString& GetString() const { return mIdentifier.get_nsCString(); }
+
+    int32_t GetInt() const { return mIdentifier.get_int32_t(); }
+
+    PluginIdentifier GetIdentifier() const { return mIdentifier; }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
+
+    PluginIdentifier mIdentifier;
+    nsRefPtr<StoredIdentifier> mStored;
+  };
+
+  static void ClearIdentifiers();
+
+private:
   static NPObject*
   ScriptableAllocate(NPP aInstance,
                      NPClass* aClass);
 
   static void
   ScriptableInvalidate(NPObject* aObject);
 
   static void
@@ -225,14 +286,20 @@ private:
   PluginInstanceChild* mInstance;
   NPObject* mObject;
   bool mInvalidated;
   int mProtectCount;
 
   ScriptableObjectType mType;
 
   static const NPClass sNPClass;
+
+  static StoredIdentifier* HashIdentifier(const nsCString& aIdentifier);
+  static void UnhashIdentifier(StoredIdentifier* aIdentifier);
+
+  typedef nsDataHashtable<nsCStringHashKey, nsRefPtr<StoredIdentifier>> IdentifierTable;
+  static IdentifierTable sIdentifiers;
 };
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
 #endif /* dom_plugins_PluginScriptableObjectChild_h */
--- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -1,25 +1,113 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=2 et :
  * 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 "PluginScriptableObjectParent.h"
 
+#include "jsapi.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/plugins/PluginIdentifierParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/unused.h"
 #include "nsNPAPIPlugin.h"
 #include "PluginScriptableObjectUtils.h"
 
+using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
+/**
+ * NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
+ * ensuring that string identifiers are rooted without interning them all. We
+ * assume that all NPIdentifiers passed into nsJSNPRuntime will not be used
+ * outside the scope of the NPAPI call (i.e., they won't be stored in the
+ * heap). Rooting is done using the StackIdentifier class, which roots the
+ * identifier via RootedId.
+ *
+ * This system does not allow jsids to be moved, as would be needed for
+ * generational or compacting GC. When Firefox implements a moving GC for
+ * strings, we will need to ensure that no movement happens while NPAPI code is
+ * on the stack: although StackIdentifier roots all identifiers used, the GC has
+ * no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it
+ * is moved.
+ */
+
+class MOZ_STACK_CLASS StackIdentifier
+{
+public:
+  StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern = false);
+
+  bool Failed() const { return mFailed; }
+  NPIdentifier ToNPIdentifier() const { return mIdentifier; }
+
+private:
+  bool mFailed;
+  NPIdentifier mIdentifier;
+  AutoSafeJSContext mCx;
+  JS::RootedId mId;
+};
+
+StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern)
+: mFailed(false),
+  mId(mCx)
+{
+  if (aIdentifier.type() == PluginIdentifier::TnsCString) {
+    // We don't call _getstringidentifier because we may not want to intern the string.
+    NS_ConvertUTF8toUTF16 utf16name(aIdentifier.get_nsCString());
+    JS::RootedString str(mCx, JS_NewUCStringCopyN(mCx, utf16name.get(), utf16name.Length()));
+    if (!str) {
+      NS_ERROR("Id can't be allocated");
+      mFailed = true;
+      return;
+    }
+    if (aIntern) {
+      str = JS_InternJSString(mCx, str);
+      if (!str) {
+        NS_ERROR("Id can't be allocated");
+        mFailed = true;
+        return;
+      }
+    }
+    if (!JS_StringToId(mCx, str, &mId)) {
+      NS_ERROR("Id can't be allocated");
+      mFailed = true;
+      return;
+    }
+    mIdentifier = JSIdToNPIdentifier(mId);
+    return;
+  }
+
+  mIdentifier = mozilla::plugins::parent::_getintidentifier(aIdentifier.get_int32_t());
+}
+
+static bool
+FromNPIdentifier(NPIdentifier aIdentifier, PluginIdentifier* aResult)
+{
+  if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
+    nsCString string;
+    NPUTF8* chars =
+      mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
+    if (!chars) {
+      return false;
+    }
+    string.Adopt(chars);
+    *aResult = PluginIdentifier(string);
+    return true;
+  }
+  else {
+    int32_t intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
+    *aResult = PluginIdentifier(intval);
+    return true;
+  }
+}
+
 namespace {
 
 inline void
 ReleaseVariant(NPVariant& aVariant,
                PluginInstanceParent* aInstance)
 {
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance);
   if (npn) {
@@ -100,18 +188,18 @@ PluginScriptableObjectParent::Scriptable
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
   if (!actor) {
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
   if (!actor->CallHasMethod(identifier, &result)) {
     NS_WARNING("Failed to send message!");
@@ -140,18 +228,18 @@ PluginScriptableObjectParent::Scriptable
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
   if (!actor) {
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance());
   if (!args.IsOk()) {
     NS_ERROR("Failed to convert arguments!");
@@ -242,18 +330,18 @@ PluginScriptableObjectParent::Scriptable
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
   if (!actor) {
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool result;
   if (!actor->CallHasProperty(identifier, &result)) {
     NS_WARNING("Failed to send message!");
@@ -291,18 +379,18 @@ PluginScriptableObjectParent::Scriptable
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
   if (!actor) {
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   ProtectedVariant value(*aValue, actor->GetInstance());
   if (!value.IsOk()) {
     NS_WARNING("Failed to convert variant!");
@@ -334,18 +422,18 @@ PluginScriptableObjectParent::Scriptable
     return false;
   }
 
   ProtectedActor<PluginScriptableObjectParent> actor(object->parent);
   if (!actor) {
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   bool success;
   if (!actor->CallRemoveProperty(identifier, &success)) {
     NS_WARNING("Failed to send message!");
@@ -380,17 +468,17 @@ PluginScriptableObjectParent::Scriptable
   NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
 
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(aObject);
   if (!npn) {
     NS_ERROR("No netscape funcs!");
     return false;
   }
 
-  AutoInfallibleTArray<PPluginIdentifierParent*, 10> identifiers;
+  AutoInfallibleTArray<PluginIdentifier, 10> identifiers;
   bool success;
   if (!actor->CallEnumerate(&identifiers, &success)) {
     NS_WARNING("Failed to send message!");
     return false;
   }
 
   if (!success) {
     return false;
@@ -404,19 +492,23 @@ PluginScriptableObjectParent::Scriptable
 
   *aIdentifiers = (NPIdentifier*)npn->memalloc(*aCount * sizeof(NPIdentifier));
   if (!*aIdentifiers) {
     NS_ERROR("Out of memory!");
     return false;
   }
 
   for (uint32_t index = 0; index < *aCount; index++) {
-    PluginIdentifierParent* id =
-      static_cast<PluginIdentifierParent*>(identifiers[index]);
-    (*aIdentifiers)[index] = id->ToNPIdentifier();
+    // We intern the ID to avoid a GC hazard here. This could probably be fixed
+    // if the interface with nsJSNPRuntime were smarter.
+    StackIdentifier stackID(identifiers[index], true /* aIntern */);
+    if (stackID.Failed()) {
+      return false;
+    }
+    (*aIdentifiers)[index] = stackID.ToNPIdentifier();
   }
   return true;
 }
 
 // static
 bool
 PluginScriptableObjectParent::ScriptableConstruct(NPObject* aObject,
                                                   const NPVariant* aArgs,
@@ -644,17 +736,17 @@ PluginScriptableObjectParent::DropNPObje
 
 void
 PluginScriptableObjectParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Implement me! Bug 1005163
 }
 
 bool
-PluginScriptableObjectParent::AnswerHasMethod(PPluginIdentifierParent* aId,
+PluginScriptableObjectParent::AnswerHasMethod(const PluginIdentifier& aId,
                                               bool* aHasMethod)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
     *aHasMethod = false;
     return true;
   }
 
@@ -670,23 +762,27 @@ PluginScriptableObjectParent::AnswerHasM
 
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aHasMethod = false;
     return true;
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
-  *aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, id->ToNPIdentifier());
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aHasMethod = false;
+    return true;
+  }
+  *aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, stackID.ToNPIdentifier());
   return true;
 }
 
 bool
-PluginScriptableObjectParent::AnswerInvoke(PPluginIdentifierParent* aId,
+PluginScriptableObjectParent::AnswerInvoke(const PluginIdentifier& aId,
                                            const InfallibleTArray<Variant>& aArgs,
                                            Variant* aResult,
                                            bool* aSuccess)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerInvoke with an invalidated object!");
     *aResult = void_t();
     *aSuccess = false;
@@ -707,16 +803,23 @@ PluginScriptableObjectParent::AnswerInvo
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aResult = void_t();
+    *aSuccess = false;
+    return true;
+  }
+
   AutoFallibleTArray<NPVariant, 10> convertedArgs;
   uint32_t argCount = aArgs.Length();
 
   if (!convertedArgs.SetLength(argCount)) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
@@ -728,19 +831,18 @@ PluginScriptableObjectParent::AnswerInvo
         ReleaseVariant(convertedArgs[index], instance);
       }
       *aResult = void_t();
       *aSuccess = false;
       return true;
     }
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
   NPVariant result;
-  bool success = npn->invoke(instance->GetNPP(), mObject, id->ToNPIdentifier(),
+  bool success = npn->invoke(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
                              convertedArgs.Elements(), argCount, &result);
 
   for (uint32_t index = 0; index < argCount; index++) {
     ReleaseVariant(convertedArgs[index], instance);
   }
 
   if (!success) {
     *aResult = void_t();
@@ -843,17 +945,17 @@ PluginScriptableObjectParent::AnswerInvo
   }
 
   *aResult = convertedResult;
   *aSuccess = true;
   return true;
 }
 
 bool
-PluginScriptableObjectParent::AnswerHasProperty(PPluginIdentifierParent* aId,
+PluginScriptableObjectParent::AnswerHasProperty(const PluginIdentifier& aId,
                                                 bool* aHasProperty)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
     *aHasProperty = false;
     return true;
   }
 
@@ -869,25 +971,30 @@ PluginScriptableObjectParent::AnswerHasP
 
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aHasProperty = false;
     return true;
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aHasProperty = false;
+    return true;
+  }
+
   *aHasProperty = npn->hasproperty(instance->GetNPP(), mObject,
-                                   id->ToNPIdentifier());
+                                   stackID.ToNPIdentifier());
   return true;
 }
 
 bool
 PluginScriptableObjectParent::AnswerGetParentProperty(
-                                                   PPluginIdentifierParent* aId,
+                                                   const PluginIdentifier& aId,
                                                    Variant* aResult,
                                                    bool* aSuccess)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerGetProperty with an invalidated object!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
@@ -907,19 +1014,25 @@ PluginScriptableObjectParent::AnswerGetP
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aResult = void_t();
+    *aSuccess = false;
+    return true;
+  }
+
   NPVariant result;
-  if (!npn->getproperty(instance->GetNPP(), mObject, id->ToNPIdentifier(),
+  if (!npn->getproperty(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
                         &result)) {
     *aResult = void_t();
     *aSuccess = false;
     return true;
   }
 
   Variant converted;
   if ((*aSuccess = ConvertToRemoteVariant(result, converted, instance))) {
@@ -929,17 +1042,17 @@ PluginScriptableObjectParent::AnswerGetP
   else {
     *aResult = void_t();
   }
 
   return true;
 }
 
 bool
-PluginScriptableObjectParent::AnswerSetProperty(PPluginIdentifierParent* aId,
+PluginScriptableObjectParent::AnswerSetProperty(const PluginIdentifier& aId,
                                                 const Variant& aValue,
                                                 bool* aSuccess)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
     *aSuccess = false;
     return true;
   }
@@ -962,26 +1075,31 @@ PluginScriptableObjectParent::AnswerSetP
   }
 
   NPVariant converted;
   if (!ConvertToVariant(aValue, converted, instance)) {
     *aSuccess = false;
     return true;
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aSuccess = false;
+    return true;
+  }
+
   if ((*aSuccess = npn->setproperty(instance->GetNPP(), mObject,
-                                    id->ToNPIdentifier(), &converted))) {
+                                    stackID.ToNPIdentifier(), &converted))) {
     ReleaseVariant(converted, instance);
   }
   return true;
 }
 
 bool
-PluginScriptableObjectParent::AnswerRemoveProperty(PPluginIdentifierParent* aId,
+PluginScriptableObjectParent::AnswerRemoveProperty(const PluginIdentifier& aId,
                                                    bool* aSuccess)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
     *aSuccess = false;
     return true;
   }
 
@@ -997,24 +1115,29 @@ PluginScriptableObjectParent::AnswerRemo
 
   const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance);
   if (!npn) {
     NS_ERROR("No netscape funcs?!");
     *aSuccess = false;
     return true;
   }
 
-  PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
+  StackIdentifier stackID(aId);
+  if (stackID.Failed()) {
+    *aSuccess = false;
+    return true;
+  }
+
   *aSuccess = npn->removeproperty(instance->GetNPP(), mObject,
-                                  id->ToNPIdentifier());
+                                  stackID.ToNPIdentifier());
   return true;
 }
 
 bool
-PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PPluginIdentifierParent*>* aProperties,
+PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
                                               bool* aSuccess)
 {
   if (!mObject) {
     NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
     *aSuccess = false;
     return true;
   }
 
@@ -1039,31 +1162,22 @@ PluginScriptableObjectParent::AnswerEnum
   uint32_t idCount;
   if (!npn->enumerate(instance->GetNPP(), mObject, &ids, &idCount)) {
     *aSuccess = false;
     return true;
   }
 
   aProperties->SetCapacity(idCount);
 
-  mozilla::AutoSafeJSContext cx;
   for (uint32_t index = 0; index < idCount; index++) {
-    // Because of GC hazards, all identifiers returned from enumerate
-    // must be made permanent.
-    if (_identifierisstring(ids[index])) {
-      JS::Rooted<JSString*> str(cx, NPIdentifierToString(ids[index]));
-      if (!JS_StringHasBeenInterned(cx, str)) {
-        DebugOnly<JSString*> str2 = JS_InternJSString(cx, str);
-        NS_ASSERTION(str2 == str, "Interning a JS string which is currently an ID should return itself.");
-      }
+    PluginIdentifier id;
+    if (!FromNPIdentifier(ids[index], &id)) {
+      return false;
     }
-    PluginIdentifierParent* id =
-      instance->Module()->GetIdentifierForNPIdentifier(instance->GetNPP(), ids[index]);
     aProperties->AppendElement(id);
-    NS_ASSERTION(!id->IsTemporary(), "Should only have permanent identifiers!");
   }
 
   npn->memfree(ids);
   *aSuccess = true;
   return true;
 }
 
 bool
@@ -1224,18 +1338,18 @@ PluginScriptableObjectParent::GetPropert
   NS_ASSERTION(Type() == Proxy, "Bad type!");
 
   ParentNPObject* object = static_cast<ParentNPObject*>(mObject);
   if (object->invalidated) {
     NS_WARNING("Calling method on an invalidated object!");
     return false;
   }
 
-  PluginIdentifierParent::StackIdentifier identifier(GetInstance(), aName);
-  if (!identifier) {
+  PluginIdentifier identifier;
+  if (!FromNPIdentifier(aName, &identifier)) {
     return false;
   }
 
   bool hasProperty, hasMethod, success;
   Variant result;
   if (!CallGetChildProperty(identifier, &hasProperty, &hasMethod, &result,
                             &success)) {
     return false;
--- a/dom/plugins/ipc/PluginScriptableObjectParent.h
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.h
@@ -13,17 +13,16 @@
 #include "npfunctions.h"
 #include "npruntime.h"
 
 namespace mozilla {
 namespace plugins {
 
 class PluginInstanceParent;
 class PluginScriptableObjectParent;
-class PPluginIdentifierParent;
 
 struct ParentNPObject : NPObject
 {
   ParentNPObject()
     : NPObject(), parent(nullptr), invalidated(false) { }
 
   // |parent| is always valid as long as the actor is alive. Once the actor is
   // destroyed this will be set to null.
@@ -44,50 +43,50 @@ public:
 
   void
   InitializeLocal(NPObject* aObject);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerHasMethod(PPluginIdentifierParent* aId,
+  AnswerHasMethod(const PluginIdentifier& aId,
                   bool* aHasMethod) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerInvoke(PPluginIdentifierParent* aId,
+  AnswerInvoke(const PluginIdentifier& aId,
                const InfallibleTArray<Variant>& aArgs,
                Variant* aResult,
                bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
   AnswerInvokeDefault(const InfallibleTArray<Variant>& aArgs,
                       Variant* aResult,
                       bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerHasProperty(PPluginIdentifierParent* aId,
+  AnswerHasProperty(const PluginIdentifier& aId,
                     bool* aHasProperty) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerGetParentProperty(PPluginIdentifierParent* aId,
+  AnswerGetParentProperty(const PluginIdentifier& aId,
                           Variant* aResult,
                           bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerSetProperty(PPluginIdentifierParent* aId,
+  AnswerSetProperty(const PluginIdentifier& aId,
                     const Variant& aValue,
                     bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerRemoveProperty(PPluginIdentifierParent* aId,
+  AnswerRemoveProperty(const PluginIdentifier& aId,
                        bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
-  AnswerEnumerate(InfallibleTArray<PPluginIdentifierParent*>* aProperties,
+  AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
                   bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
   AnswerConstruct(const InfallibleTArray<Variant>& aArgs,
                   Variant* aResult,
                   bool* aSuccess) MOZ_OVERRIDE;
 
   virtual bool
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace plugins {
+
+union PluginIdentifier
+{
+  nsCString;
+  int32_t;
+};
+
+} // namespace plugins
+} // namespace mozilla
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -16,18 +16,16 @@ EXPORTS.mozilla.plugins += [
     'BrowserStreamChild.h',
     'BrowserStreamParent.h',
     'ChildAsyncCall.h',
     'ChildTimer.h',
     'NPEventAndroid.h',
     'NPEventOSX.h',
     'NPEventUnix.h',
     'NPEventWindows.h',
-    'PluginIdentifierChild.h',
-    'PluginIdentifierParent.h',
     'PluginInstanceChild.h',
     'PluginInstanceParent.h',
     'PluginMessageUtils.h',
     'PluginModuleChild.h',
     'PluginModuleParent.h',
     'PluginProcessChild.h',
     'PluginProcessParent.h',
     'PluginScriptableObjectChild.h',
@@ -74,18 +72,16 @@ if CONFIG['MOZ_ENABLE_QT']:
     ]
 
 UNIFIED_SOURCES += [
     'BrowserStreamChild.cpp',
     'BrowserStreamParent.cpp',
     'ChildAsyncCall.cpp',
     'ChildTimer.cpp',
     'PluginBackgroundDestroyer.cpp',
-    'PluginIdentifierChild.cpp',
-    'PluginIdentifierParent.cpp',
     'PluginInstanceParent.cpp',
     'PluginMessageUtils.cpp',
     'PluginModuleParent.cpp',
     'PluginProcessChild.cpp',
     'PluginProcessParent.cpp',
     'PluginScriptableObjectChild.cpp',
     'PluginScriptableObjectParent.cpp',
     'PluginStreamChild.cpp',
@@ -100,18 +96,18 @@ SOURCES += [
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'PluginInterposeOSX.mm',
         'PluginUtilsOSX.mm',
     ]
 
 IPDL_SOURCES += [
     'PBrowserStream.ipdl',
+    'PluginTypes.ipdlh',
     'PPluginBackgroundDestroyer.ipdl',
-    'PPluginIdentifier.ipdl',
     'PPluginInstance.ipdl',
     'PPluginModule.ipdl',
     'PPluginScriptableObject.ipdl',
     'PPluginStream.ipdl',
     'PPluginSurface.ipdl',
     'PStreamNotify.ipdl',
 ]
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -308,16 +308,18 @@ Promise::Promise(nsIGlobalObject* aGloba
   , mState(Pending)
   , mTaskPending(false)
   , mHadRejectCallback(false)
   , mResolvePending(false)
 {
   MOZ_ASSERT(mGlobal);
 
   mozilla::HoldJSObjects(this);
+
+  mCreationTimestamp = TimeStamp::Now();
 }
 
 Promise::~Promise()
 {
   MaybeReportRejectedOnce();
   mozilla::DropJSObjects(this);
 }
 
@@ -1167,16 +1169,17 @@ Promise::RunResolveTask(JS::Handle<JS::V
   // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState
   // from asserting.
   if (mState != Pending) {
     return;
   }
 
   SetResult(aValue);
   SetState(aState);
+  mSettlementTimestamp = TimeStamp::Now();
 
   // If the Promise was rejected, and there is no reject handler already setup,
   // watch for thread shutdown.
   if (aState == PromiseState::Rejected &&
       !mHadRejectCallback &&
       !NS_IsMainThread()) {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(worker);
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Promise_h
 #define mozilla_dom_Promise_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/WeakPtr.h"
 #include "nsWrapperCache.h"
 #include "nsAutoPtr.h"
@@ -317,14 +318,20 @@ private:
 
   bool mResolvePending;
 
   // If a rejected promise on a worker has no reject callbacks attached, it
   // needs to know when the worker is shutting down, to report the error on the
   // console before the worker's context is deleted. This feature is used for
   // that purpose.
   nsAutoPtr<PromiseReportRejectFeature> mFeature;
+
+  // The time when this promise was created.
+  TimeStamp mCreationTimestamp;
+
+  // The time when this promise transitioned out of the pending state.
+  TimeStamp mSettlementTimestamp;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Promise_h
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -3,16 +3,17 @@
 /* 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 "mozilla/dom/PromiseDebugging.h"
 
 #include "js/Value.h"
 
+#include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseDebuggingBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ void
@@ -59,10 +60,28 @@ PromiseDebugging::GetFullfillmentStack(G
 
 /* static */ void
 PromiseDebugging::GetDependentPromises(GlobalObject&, Promise& aPromise,
                                        nsTArray<nsRefPtr<Promise>>& aPromises)
 {
   aPromise.GetDependentPromises(aPromises);
 }
 
+/* static */ double
+PromiseDebugging::GetPromiseLifetime(GlobalObject&, Promise& aPromise)
+{
+  return (TimeStamp::Now() - aPromise.mCreationTimestamp).ToMilliseconds();
+}
+
+/* static */ double
+PromiseDebugging::GetTimeToSettle(GlobalObject&, Promise& aPromise,
+                                  ErrorResult& aRv)
+{
+  if (aPromise.mState == Promise::Pending) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return 0;
+  }
+  return (aPromise.mSettlementTimestamp -
+          aPromise.mCreationTimestamp).ToMilliseconds();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/PromiseDebugging.h
+++ b/dom/promise/PromiseDebugging.h
@@ -7,16 +7,19 @@
 #ifndef mozilla_dom_PromiseDebugging_h
 #define mozilla_dom_PromiseDebugging_h
 
 #include "js/TypeDecls.h"
 #include "nsTArray.h"
 #include "nsRefPtr.h"
 
 namespace mozilla {
+
+class ErrorResult;
+
 namespace dom {
 
 class Promise;
 struct PromiseDebuggingStateHolder;
 class GlobalObject;
 
 class PromiseDebugging
 {
@@ -27,14 +30,17 @@ public:
   static void GetAllocationStack(GlobalObject&, Promise& aPromise,
                                  JS::MutableHandle<JSObject*> aStack);
   static void GetRejectionStack(GlobalObject&, Promise& aPromise,
                                 JS::MutableHandle<JSObject*> aStack);
   static void GetFullfillmentStack(GlobalObject&, Promise& aPromise,
                                    JS::MutableHandle<JSObject*> aStack);
   static void GetDependentPromises(GlobalObject&, Promise& aPromise,
                                    nsTArray<nsRefPtr<Promise>>& aPromises);
+  static double GetPromiseLifetime(GlobalObject&, Promise& aPromise);
+  static double GetTimeToSettle(GlobalObject&, Promise& aPromise,
+                                ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseDebugging_h
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PromiseWorkerProxy_h
 #define mozilla_dom_PromiseWorkerProxy_h
 
+// Required for Promise::PromiseTaskSync.
+#include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
@@ -38,16 +40,26 @@ class WorkerPrivate;
 //
 //   3. In your WorkerMainThreadRunnable::MainThreadRun(), obtain a Promise on
 //      the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
 //      to bind the PromiseWorkerProxy created at #2.
 //
 //   4. Then the Promise results returned by ResolvedCallback/RejectedCallback
 //      will be dispatched as a WorkerRunnable to the worker thread to
 //      resolve/reject the Promise created at #1.
+//
+// PromiseWorkerProxy can also be used in situations where there is no main
+// thread Promise, or where special handling is required on the worker thread
+// for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
+// 2 above. When the main thread is ready to resolve the worker thread promise,
+// dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
+// worker. This might be null! In the WorkerRunnable's WorkerRun() use
+// GetWorkerPromise() to access the Promise and resolve/reject it. Then call
+// CleanUp() on the worker
+// thread.
 
 class PromiseWorkerProxy : public PromiseNativeHandler,
                            public workers::WorkerFeature
 {
   friend class PromiseWorkerProxyRunnable;
 
   // This overrides the non-threadsafe refcounting in PromiseNativeHandler.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PromiseWorkerProxy)
--- a/dom/telephony/test/marionette/test_mmi_call_forwarding.js
+++ b/dom/telephony/test/marionette/test_mmi_call_forwarding.js
@@ -27,59 +27,31 @@ function waitForManagerEvent(aEventName)
     ok(true, "MobileConnection event '" + aEventName + "' got.");
     deferred.resolve(aEvent);
   });
 
   return deferred.promise;
 }
 
 /**
- * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
- *
- * Fulfill params: A DOMEvent.
- * Reject params: A DOMEvent.
- *
- * @param aRequest
- *        A DOMRequest instance.
- *
- * @return A deferred promise.
- */
-function wrapDomRequestAsPromise(aRequest) {
-  let deferred = Promise.defer();
-
-  ok(aRequest instanceof DOMRequest,
-     "aRequest is instanceof " + aRequest.constructor);
-
-  aRequest.addEventListener("success", function(aEvent) {
-    deferred.resolve(aEvent);
-  });
-  aRequest.addEventListener("error", function(aEvent) {
-    deferred.reject(aEvent);
-  });
-
-  return deferred.promise;
-}
-
-/**
  * Configures call forward options.
  *
  * Fulfill params: (none)
  * Reject params:
  *   'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter', or
  *   'GenericFailure'.
  *
  * @param aOptions
  *        A MozCallForwardingOptions.
  *
  * @return A deferred promise.
  */
 function setCallForwardingOption(aOptions) {
   let request = connection.setCallForwardingOption(aOptions);
-  return wrapDomRequestAsPromise(request)
-    .then(null, () => { throw request.error; });
+  return request.then(null, () => { throw request.error; });
 }
 
 const TEST_DATA = [
   {
     reason: MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL,
     number: "+886912345678",
     serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE,
     timeSeconds: 5
--- a/dom/tests/mochitest/chrome/test_clonewrapper.xul
+++ b/dom/tests/mochitest/chrome/test_clonewrapper.xul
@@ -59,26 +59,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   // Tests cloning between chrome and content.
   function runChromeContentTest(contentWin) {
 
     // We should be able to clone a content object.
     tryToClone(contentWin.wrappedJSObject.testObject,
                true,
                "Chrome should be able to clone content object");
 
-    // COWs without __exposedProps__ silently deny property gets. So Content
-    // should be able to clone a COW, but the result of the clone should be empty.
-    var p = contentWin.wrappedJSObject.tryToClone(window.testObject, true,
-                                                  "Content should be able to clone COW");
-    return new Promise(function(resolve) {
-      p.then(function(cloneResult) {
-        is(Cu.waiveXrays(cloneResult).toSource(), "({})", "Empty COW clone");
-        resolve();
-      });
-    });
+    return Promise.resolve();
   }
 
   // Test cloning between content and content.
   //
   // Note - the way we do this is kind of sketchy. Because we're grabbing the
   // test object from win1 by waiving Xray (via .wrappedJSObject), the object
   // we're passing from win1 to win2 is actually the waived object (which has
   // a distinct identity in the compartment of win2). So this means that we're
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -1,7 +1,9 @@
 [DEFAULT]
 support-files =
   test_headers_common.js
   test_headers_mainthread.js
+  worker_test_fetch_basic.js
   worker_wrapper.js
 
 [test_headers.html]
+[test_fetch_basic.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_fetch_basic.html
@@ -0,0 +1,57 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1039846 - Test fetch() function in worker</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script type="text/javascript" src="worker_test_fetch_basic.js"> </script>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function testOnWorker(done) {
+  ok(true, "=== Start Worker Tests ===");
+  var worker = new Worker("worker_test_fetch_basic.js");
+  worker.onmessage = function(event) {
+    if (event.data.type == "finish") {
+      ok(true, "=== Finish Worker Tests ===");
+      done();
+    } else if (event.data.type == "status") {
+      ok(event.data.status, event.data.msg);
+    }
+  }
+
+  worker.onerror = function(event) {
+    ok(false, "Worker had an error: " + event.data);
+    ok(true, "=== Finish Worker Tests ===");
+    done();
+  };
+
+  worker.postMessage("start");
+}
+
+//
+// Driver
+//
+
+SpecialPowers.pushPrefEnv({"set": [
+  ["dom.fetch.enabled", true]
+]}, function() {
+  testOnWorker(function() {
+    SimpleTest.finish();
+  });
+});
+</script>
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/worker_test_fetch_basic.js
@@ -0,0 +1,94 @@
+if (typeof ok !== "function") {
+  function ok(a, msg) {
+    dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
+    postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+  }
+}
+
+if (typeof is !== "function") {
+  function is(a, b, msg) {
+    dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+    postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+  }
+}
+
+function testAboutURL() {
+  var p1 = fetch('about:blank').then(function(res) {
+    is(res.status, 200, "about:blank should load a valid Response");
+    is(res.headers.get('content-type'), 'text/html;charset=utf-8',
+       "about:blank content-type should be text/html;charset=utf-8");
+    return res.text().then(function(v) {
+      is(v, "", "about:blank body should be empty");
+    });
+  });
+
+  var p2 = fetch('about:config').then(function(res) {
+    is(res.type, "error", "about:config should fail");
+  });
+
+  return Promise.all([p1, p2]);
+}
+
+function testDataURL() {
+  return fetch("data:text/plain;charset=UTF-8,Hello").then(function(res) {
+    ok(true, "Data URL fetch should resolve");
+    if (res.type == "error") {
+      ok(false, "Data URL fetch should not fail.");
+      return Promise.reject();
+    }
+    ok(res instanceof Response, "Fetch should resolve to a Response");
+    is(res.status, 200, "Data URL status should be 200");
+    is(res.statusText, "OK", "Data URL statusText should be OK");
+    ok(res.headers.has("content-type"), "Headers must have Content-Type header");
+    is(res.headers.get("content-type"), "text/plain;charset=UTF-8", "Content-Type header should match specified value");
+    return res.text().then(function(body) {
+      is(body, "Hello", "Data URL Body should match");
+    });
+  });
+}
+
+function testSameOriginBlobURL() {
+  var blob = new Blob(["english ", "sentence"], { type: "text/plain" });
+  var url = URL.createObjectURL(blob);
+  return fetch(url).then(function(res) {
+    ok(true, "Blob URL fetch should resolve");
+    if (res.type == "error") {
+      ok(false, "Blob URL fetch should not fail.");
+      return Promise.reject();
+    }
+    ok(res instanceof Response, "Fetch should resolve to a Response");
+    is(res.status, 200, "Blob fetch status should be 200");
+    is(res.statusText, "OK", "Blob fetch statusText should be OK");
+    ok(res.headers.has("content-type"), "Headers must have Content-Type header");
+    is(res.headers.get("content-type"), blob.type, "Content-Type header should match specified value");
+    ok(res.headers.has("content-length"), "Headers must have Content-Length header");
+    is(parseInt(res.headers.get("content-length")), 16, "Content-Length should match Blob's size");
+    return res.text().then(function(body) {
+      is(body, "english sentence", "Blob fetch body should match");
+    });
+  });
+}
+
+function runTest() {
+  var done = function() {
+    if (typeof SimpleTest === "object") {
+      SimpleTest.finish();
+    } else {
+      postMessage({ type: 'finish' });
+    }
+  }
+
+  Promise.resolve()
+    .then(testAboutURL)
+    .then(testDataURL)
+    .then(testSameOriginBlobURL)
+    //.then(testAboutURL)
+    // Put more promise based tests here.
+    .then(done)
+    .catch(function(e) {
+      ok(false, "Some Response tests failed " + e);
+      done();
+    })
+}
+
+onmessage = runTest;
--- a/dom/webidl/PromiseDebugging.webidl
+++ b/dom/webidl/PromiseDebugging.webidl
@@ -50,9 +50,22 @@ interface PromiseDebugging {
    * Once a promise is settled, it will generally notify its dependent promises
    * and forget about them, so this is most useful on unsettled promises.
    *
    * Note that this function only returns the promises that directly depend on
    * p.  It does not recursively return promises that depend on promises that
    * depend on p.
    */
   static sequence<Promise<any>> getDependentPromises(Promise<any> p);
+
+  /**
+   * Get the number of milliseconds elapsed since the given promise was created.
+   */
+  static DOMHighResTimeStamp getPromiseLifetime(Promise<any> p);
+
+  /*
+   * Get the number of milliseconds elapsed between the promise being created
+   * and being settled.  Throws NS_ERROR_UNEXPECTED if the promise has not
+   * settled.
+   */
+  [Throws]
+  static DOMHighResTimeStamp getTimeToSettle(Promise<any> p);
 };
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -1,9 +1,9 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: IDL; tab-width: 1; 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/.
  *
  * The origin of this IDL file is
  * https://fetch.spec.whatwg.org/#request-class
  */
 
@@ -29,10 +29,10 @@ Request implements Body;
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
   BodyInit body;
   RequestMode mode;
   RequestCredentials credentials;
 };
 
-enum RequestMode { "same-origin", "no-cors", "cors" };
+enum RequestMode { "same-origin", "no-cors", "cors", "cors-with-forced-preflight" };
 enum RequestCredentials { "omit", "same-origin", "include" };
--- a/dom/webidl/URLSearchParams.webidl
+++ b/dom/webidl/URLSearchParams.webidl
@@ -8,20 +8,21 @@
  *
  * To the extent possible under law, the editors have waived all copyright
  * and related or neighboring rights to this work. In addition, as of 17
  * February 2013, the editors have made this specification available under
  * the Open Web Foundation Agreement Version 1.0, which is available at
  * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
  */
 
-[Constructor(optional DOMString init = ""),
+[Constructor(optional ScalarValueString init = ""),
  Constructor(URLSearchParams init),
  Exposed=(Window,Worker)]
 interface URLSearchParams {
-  void append(DOMString name, DOMString value);
-  void delete(DOMString name);
-  DOMString? get(DOMString name);
-  sequence<DOMString> getAll(DOMString name);
-  boolean has(DOMString name);
-  void set(DOMString name, DOMString value);
+  void append(ScalarValueString name, ScalarValueString value);
+  void delete(ScalarValueString name);
+  ScalarValueString? get(ScalarValueString name);
+  sequence<ScalarValueString> getAll(ScalarValueString name);
+  boolean has(ScalarValueString name);
+  void set(ScalarValueString name, ScalarValueString value);
+  // iterable<ScalarValueString, ScalarValueString>; - Bug 1085284
   stringifier;
 };
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -12,40 +12,40 @@
  * the Open Web Foundation Agreement Version 1.0, which is available at
  * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
  */
 
 [NoInterfaceObject,
  Exposed=(Window, Worker)]
 interface URLUtils {
   // Bug 824857: no support for stringifier attributes yet.
-  //  stringifier attribute DOMString href;
+  //  stringifier attribute ScalarValueString href;
   [Throws, CrossOriginWritable=Location]
-           attribute DOMString href;
+           attribute ScalarValueString href;
   [Throws]
-  readonly attribute DOMString origin;
+  readonly attribute ScalarValueString origin;
 
   [Throws]
-           attribute DOMString protocol;
+           attribute ScalarValueString protocol;
   [Throws]
-           attribute DOMString username;
+           attribute ScalarValueString username;
   [Throws]
-           attribute DOMString password;
+           attribute ScalarValueString password;
   [Throws]
-           attribute DOMString host;
+           attribute ScalarValueString host;
   [Throws]
-           attribute DOMString hostname;
+           attribute ScalarValueString hostname;
   [Throws]
-           attribute DOMString port;
+           attribute ScalarValueString port;
   [Throws]
-           attribute DOMString pathname;
+           attribute ScalarValueString pathname;
   [Throws]
-           attribute DOMString search;
+           attribute ScalarValueString search;
 
            attribute URLSearchParams searchParams;
 
   [Throws]
-           attribute DOMString hash;
+           attribute ScalarValueString hash;
 
   // Bug 824857 should remove this.
   [Throws]
   stringifier;
 };
--- a/dom/webidl/URLUtilsReadOnly.webidl
+++ b/dom/webidl/URLUtilsReadOnly.webidl
@@ -12,19 +12,19 @@
  * the Open Web Foundation Agreement Version 1.0, which is available at
  * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
  */
 
 [NoInterfaceObject,
  Exposed=(Window, Worker)]
 interface URLUtilsReadOnly {
   stringifier;
-  readonly attribute DOMString href;
+  readonly attribute ScalarValueString href;
 
-  readonly attribute DOMString protocol;
-  readonly attribute DOMString host;
-  readonly attribute DOMString hostname;
-  readonly attribute DOMString port;
-  readonly attribute DOMString pathname;
-  readonly attribute DOMString search;
-  readonly attribute DOMString hash;
-  readonly attribute DOMString origin;
+  readonly attribute ScalarValueString protocol;
+  readonly attribute ScalarValueString host;
+  readonly attribute ScalarValueString hostname;
+  readonly attribute ScalarValueString port;
+  readonly attribute ScalarValueString pathname;
+  readonly attribute ScalarValueString search;
+  readonly attribute ScalarValueString hash;
+  readonly attribute ScalarValueString origin;
 };
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerScope.h"
 
 #include "jsapi.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
 #include "mozilla/Services.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsIDocument.h"
@@ -300,18 +301,17 @@ WorkerGlobalScope::GetPerformance()
 
   return mPerformance;
 }
 
 already_AddRefed<Promise>
 WorkerGlobalScope::Fetch(const RequestOrScalarValueString& aInput,
                          const RequestInit& aInit, ErrorResult& aRv)
 {
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
+  return FetchRequest(this, aInput, aInit, aRv);
 }
 
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
 : WorkerGlobalScope(aWorkerPrivate)
 {
 }
 
 JSObject*
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -300,17 +300,17 @@ XMLDocument::Load(const nsAString& aUrl,
   }
 
   WarnOnceAbout(nsIDocument::eUseOfDOM3LoadMethod);
 
   nsCOMPtr<nsIDocument> callingDoc = GetEntryDocument();
   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
 
   // The callingDoc's Principal and doc's Principal should be the same
-  if (callingDoc->NodePrincipal() != principal) {
+  if (callingDoc && (callingDoc->NodePrincipal() != principal)) {
     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                     NS_LITERAL_CSTRING("DOM"),
                                     callingDoc,
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "XMLDocumentLoadPrincipalMismatch");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
   }
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -186,18 +186,19 @@ ImageClientSingle::UpdateImageInternal(I
     return true;
   }
 
   AutoRemoveTexture autoRemoveTexture(this);
 
   if (image->AsSharedImage() && image->AsSharedImage()->GetTextureClient(this)) {
     // fast path: no need to allocate and/or copy image data
     RefPtr<TextureClient> texture = image->AsSharedImage()->GetTextureClient(this);
-
-    autoRemoveTexture.mTexture = mFrontBuffer;
+    if (texture != mFrontBuffer) {
+      autoRemoveTexture.mTexture = mFrontBuffer;
+    }
     mFrontBuffer = texture;
     if (!AddTextureClient(texture)) {
       mFrontBuffer = nullptr;
       return false;
     }
     GetForwarder()->UpdatedTexture(this, texture, nullptr);
     GetForwarder()->UseTexture(this, texture);
   } else if (image->GetFormat() == ImageFormat::PLANAR_YCBCR) {
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -33,16 +33,19 @@ SurfaceFormatForAndroidPixelFormat(andro
   case android::PIXEL_FORMAT_RGB_565:
     return gfx::SurfaceFormat::R5G6B5;
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
     return gfx::SurfaceFormat::R8G8B8A8; // yup, use SurfaceFormat::R8G8B8A8 even though it's a YUV texture. This is an external texture.
   default:
     if (aFormat >= 0x100 && aFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       return gfx::SurfaceFormat::R8G8B8A8;
     } else {
       // This is not super-unreachable, there's a bunch of hypothetical pixel
       // formats we don't deal with.
@@ -61,16 +64,19 @@ TextureTargetForAndroidPixelFormat(andro
 {
   switch (aFormat) {
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
     return LOCAL_GL_TEXTURE_EXTERNAL;
   case android::PIXEL_FORMAT_BGRA_8888:
   case android::PIXEL_FORMAT_RGBA_8888:
   case android::PIXEL_FORMAT_RGBX_8888:
   case android::PIXEL_FORMAT_RGB_565:
     return LOCAL_GL_TEXTURE_2D;
   default:
     if (aFormat >= 0x100 && aFormat <= 0x1FF) {
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -532,18 +532,17 @@ static const uint64_t UnknownPropertyOp 
 bool
 JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> desc,
                                  PPropertyDescriptor *out)
 {
     out->attrs() = desc.attributes();
     if (!toVariant(cx, desc.value(), &out->value()))
         return false;
 
-    MOZ_ASSERT(desc.object());
-    if (!toObjectVariant(cx, desc.object(), &out->obj()))
+    if (!toObjectOrNullVariant(cx, desc.object(), &out->obj()))
         return false;
 
     if (!desc.getter()) {
         out->getter() = 0;
     } else if (desc.hasGetterObject()) {
         JSObject *getter = desc.getterObject();
         ObjectVariant objVar;
         if (!toObjectVariant(cx, getter, &objVar))
@@ -590,21 +589,17 @@ UnknownStrictPropertyStub(JSContext *cx,
 
 bool
 JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
                                MutableHandle<JSPropertyDescriptor> out)
 {
     out.setAttributes(in.attrs());
     if (!fromVariant(cx, in.value(), out.value()))
         return false;
-    Rooted<JSObject*> obj(cx);
-    obj = fromObjectVariant(cx, in.obj());
-    if (!obj)
-        return false;
-    out.object().set(obj);
+    out.object().set(fromObjectOrNullVariant(cx, in.obj()));
 
     if (in.getter().type() == GetterSetter::Tuint64_t && !in.getter().get_uint64_t()) {
         out.setGetter(nullptr);
     } else if (in.attrs() & JSPROP_GETTER) {
         Rooted<JSObject*> getter(cx);
         getter = fromObjectVariant(cx, in.getter().get_ObjectVariant());
         if (!getter)
             return false;
@@ -629,16 +624,41 @@ JavaScriptShared::toDescriptor(JSContext
             out.setSetter(JS_StrictPropertyStub);
         else
             out.setSetter(UnknownStrictPropertyStub);
     }
 
     return true;
 }
 
+bool
+JavaScriptShared::toObjectOrNullVariant(JSContext *cx, JSObject *obj, ObjectOrNullVariant *objVarp)
+{
+    if (!obj) {
+        *objVarp = NullVariant();
+        return true;
+    }
+
+    ObjectVariant objVar;
+    if (!toObjectVariant(cx, obj, &objVar))
+        return false;
+
+    *objVarp = objVar;
+    return true;
+}
+
+JSObject *
+JavaScriptShared::fromObjectOrNullVariant(JSContext *cx, ObjectOrNullVariant objVar)
+{
+    if (objVar.type() == ObjectOrNullVariant::TNullVariant)
+        return nullptr;
+
+    return fromObjectVariant(cx, objVar.get_ObjectVariant());
+}
+
 CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray<CpowEntry> &cpows)
   : js_(nullptr),
     cpows_(cpows)
 {
     // Only instantiate the CPOW manager if we might need it later.
     if (cpows.Length())
         js_ = managerGetter->GetCPOWManager();
 }
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -164,16 +164,19 @@ class JavaScriptShared
     bool toSymbolVariant(JSContext *cx, JS::Symbol *sym, SymbolVariant *symVarp);
     JS::Symbol *fromSymbolVariant(JSContext *cx, SymbolVariant symVar);
 
     bool fromDescriptor(JSContext *cx, JS::Handle<JSPropertyDescriptor> desc,
                         PPropertyDescriptor *out);
     bool toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
                       JS::MutableHandle<JSPropertyDescriptor> out);
 
+    bool toObjectOrNullVariant(JSContext *cx, JSObject *obj, ObjectOrNullVariant *objVarp);
+    JSObject *fromObjectOrNullVariant(JSContext *cx, ObjectOrNullVariant objVar);
+
     bool convertIdToGeckoString(JSContext *cx, JS::HandleId id, nsString *to);
     bool convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId id);
 
     virtual bool toObjectVariant(JSContext *cx, JSObject *obj, ObjectVariant *objVarp) = 0;
     virtual JSObject *fromObjectVariant(JSContext *cx, ObjectVariant objVar) = 0;
 
     static void ConvertID(const nsID &from, JSIID *to);
     static void ConvertID(const JSIID &from, nsID *to);
--- a/js/ipc/JavaScriptTypes.ipdlh
+++ b/js/ipc/JavaScriptTypes.ipdlh
@@ -57,16 +57,22 @@ union SymbolVariant
 {
     WellKnownSymbol;
     RegisteredSymbol;
 };
 
 struct UndefinedVariant {};
 struct NullVariant {};
 
+union ObjectOrNullVariant
+{
+    ObjectVariant;
+    NullVariant;
+};
+
 union JSVariant
 {
     UndefinedVariant;
     NullVariant;
     ObjectVariant;
     SymbolVariant;
     nsString;   /* StringValue(x) */
     double;     /* NumberValue(x) */
@@ -110,17 +116,17 @@ union JSParam
 union GetterSetter
 {
     uint64_t;
     ObjectVariant;
 };
 
 struct PPropertyDescriptor
 {
-    ObjectVariant obj;
+    ObjectOrNullVariant obj;
     uint32_t attrs;
     JSVariant value;
 
     // How to interpret these values depends on whether JSPROP_GETTER/SETTER
     // are set. If set, the corresponding value is a CPOW or 0 for NULL.
     // Otherwise, the following table is used:
     //
     //  0 - NULL
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -106,19 +106,16 @@ WrapperAnswer::RecvGetPropertyDescriptor
     RootedId id(cx);
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
         return fail(cx, rs);
 
-    if (!desc.object())
-        return ok(rs);
-
     if (!fromDescriptor(cx, desc, out))
         return fail(cx, rs);
 
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId &objId, const JSIDVariant &idVar,
@@ -137,22 +134,19 @@ WrapperAnswer::RecvGetOwnPropertyDescrip
 
     LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
 
     RootedId id(cx);
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
+    if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc))
         return fail(cx, rs);
 
-    if (desc.object() != obj)
-        return ok(rs);
-
     if (!fromDescriptor(cx, desc, out))
         return fail(cx, rs);
 
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvDefineProperty(const ObjectId &objId, const JSIDVariant &idVar,
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -168,21 +168,17 @@ struct Token
         return u.number.decimalPoint;
     }
 };
 
 struct CompileError {
     JSErrorReport report;
     char *message;
     ErrorArgumentsType argumentsType;
-    CompileError()
-      : message(nullptr), argumentsType(ArgumentsAreUnicode)
-    {
-        mozilla::PodZero(&report);
-    }
+    CompileError() : message(nullptr), argumentsType(ArgumentsAreUnicode) {}
     ~CompileError();
     void throwError(JSContext *cx);
 
   private:
     // CompileError owns raw allocated memory, so disable assignment and copying
     // for safety.
     void operator=(const CompileError &) MOZ_DELETE;
     CompileError(const CompileError &) MOZ_DELETE;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/proxy/bug1072817.js
@@ -0,0 +1,5 @@
+// |jit-test| error: TypeError
+var r = Proxy.revocable({}, {});
+var p = r.proxy;
+r.revoke();
+p instanceof Object;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4670,30 +4670,38 @@ extern JS_PUBLIC_API(void)
 JS_ReportOutOfMemory(JSContext *cx);
 
 /*
  * Complain when an allocation size overflows the maximum supported limit.
  */
 extern JS_PUBLIC_API(void)
 JS_ReportAllocationOverflow(JSContext *cx);
 
-struct JSErrorReport {
+class JSErrorReport
+{
+  public:
+    JSErrorReport()
+      : filename(nullptr), lineno(0), column(0), isMuted(false), linebuf(nullptr),
+        tokenptr(nullptr), uclinebuf(nullptr), uctokenptr(nullptr), flags(0), errorNumber(0),
+        ucmessage(nullptr), messageArgs(nullptr), exnType(0)
+    {}
+
     const char      *filename;      /* source file name, URL, etc., or null */
+    unsigned        lineno;         /* source line number */
+    unsigned        column;         /* zero-based column index in line */
     bool            isMuted;        /* See the comment in ReadOnlyCompileOptions. */
-    unsigned        lineno;         /* source line number */
     const char      *linebuf;       /* offending source line without final \n */
     const char      *tokenptr;      /* pointer to error token in linebuf */
     const char16_t  *uclinebuf;     /* unicode (original) line buffer */
     const char16_t  *uctokenptr;    /* unicode (original) token pointer */
     unsigned        flags;          /* error/warning, etc. */
     unsigned        errorNumber;    /* the error number, e.g. see js.msg */
     const char16_t  *ucmessage;     /* the (default) error message */
     const char16_t  **messageArgs;  /* arguments for the error message */
     int16_t         exnType;        /* One of the JSExnType constants */
-    unsigned        column;         /* zero-based column index in line */
 };
 
 /*
  * JSErrorReport flag values.  These may be freely composed.
  */
 #define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
 #define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
 #define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -50,17 +50,16 @@
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
 using mozilla::PodArrayZero;
-using mozilla::PodZero;
 using mozilla::PointerRangeSize;
 
 bool
 js::AutoCycleDetector::init()
 {
     ObjectSet &set = cx->cycleDetectorSet;
     hashsetAddPointer = set.lookupForAdd(obj);
     if (!hashsetAddPointer) {
@@ -376,17 +375,16 @@ js_ReportOutOfMemory(ThreadSafeContext *
     }
 
     /* Get the message for this error, but we don't expand any arguments. */
     const JSErrorFormatString *efs = js_GetErrorMessage(nullptr, JSMSG_OUT_OF_MEMORY);
     const char *msg = efs ? efs->format : "Out of memory";
 
     /* Fill out the report, but don't do anything that requires allocation. */
     JSErrorReport report;
-    PodZero(&report);
     report.flags = JSREPORT_ERROR;
     report.errorNumber = JSMSG_OUT_OF_MEMORY;
     PopulateReportBlame(cx, &report);
 
     /* Report the error. */
     if (JSErrorReporter onError = cx->runtime()->errorReporter) {
         AutoSuppressGC suppressGC(cx);
         onError(cx, msg, &report);
@@ -498,17 +496,16 @@ js_ReportErrorVA(JSContext *cx, unsigned
     if (checkReportFlags(cx, &flags))
         return true;
 
     message = JS_vsmprintf(format, ap);
     if (!message)
         return false;
     messagelen = strlen(message);
 
-    PodZero(&report);
     report.flags = flags;
     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
     report.ucmessage = ucmessage = InflateString(cx, message, &messagelen);
     PopulateReportBlame(cx, &report);
 
     warning = JSREPORT_IS_WARNING(report.flags);
 
     ReportError(cx, message, &report, nullptr, nullptr);
@@ -803,17 +800,16 @@ js_ReportErrorNumberVA(JSContext *cx, un
     JSErrorReport report;
     char *message;
     bool warning;
 
     if (checkReportFlags(cx, &flags))
         return true;
     warning = JSREPORT_IS_WARNING(flags);
 
-    PodZero(&report);
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
 
     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
                                  &message, &report, argumentsType, ap)) {
         return false;
     }
@@ -843,17 +839,16 @@ js_ReportErrorNumberUCArray(JSContext *c
                             void *userRef, const unsigned errorNumber,
                             const char16_t **args)
 {
     if (checkReportFlags(cx, &flags))
         return true;
     bool warning = JSREPORT_IS_WARNING(flags);
 
     JSErrorReport report;
-    PodZero(&report);
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
     report.messageArgs = args;
 
     char *message;
     va_list dummy;
     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -35,17 +35,16 @@
 #include "vm/ErrorObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::PodArrayZero;
-using mozilla::PodZero;
 
 static void
 exn_finalize(FreeOp *fop, JSObject *obj);
 
 bool
 Error(JSContext *cx, unsigned argc, Value *vp);
 
 static bool
@@ -808,17 +807,17 @@ ErrorReport::init(JSContext *cx, HandleV
         if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, &val) ||
             !ToUint32(cx, val, &column))
         {
             cx->clearPendingException();
             column = 0;
         }
 
         reportp = &ownedReport;
-        PodZero(&ownedReport);
+        new (reportp) JSErrorReport();
         ownedReport.filename = filename.ptr();
         ownedReport.lineno = lineno;
         ownedReport.exnType = int16_t(JSEXN_NONE);
         ownedReport.column = column;
         if (str) {
             // Note that using |str| for |ucmessage| here is kind of wrong,
             // because |str| is supposed to be of the format
             // |ErrorName: ErrorMessage|, and |ucmessage| is supposed to
@@ -860,17 +859,17 @@ ErrorReport::populateUncaughtExceptionRe
     va_start(ap, cx);
     populateUncaughtExceptionReportVA(cx, ap);
     va_end(ap);
 }
 
 void
 ErrorReport::populateUncaughtExceptionReportVA(JSContext *cx, va_list ap)
 {
-    PodZero(&ownedReport);
+    new (&ownedReport) JSErrorReport();
     ownedReport.flags = JSREPORT_ERROR;
     ownedReport.errorNumber = JSMSG_UNCAUGHT_EXCEPTION;
     // XXXbz this assumes the stack we have right now is still
     // related to our exception object.  It would be better if we
     // could accept a passed-in stack of some sort instead.
     NonBuiltinFrameIter iter(cx);
     if (!iter.done()) {
         ownedReport.filename = iter.scriptFilename();
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -40,17 +40,17 @@
 #endif
 
 #define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0)
 
 class JSAtom;
 struct JSErrorFormatString;
 class JSLinearString;
 struct JSJitInfo;
-struct JSErrorReport;
+class JSErrorReport;
 
 namespace JS {
 template <class T>
 class Heap;
 } /* namespace JS */
 
 namespace js {
 class JS_FRIEND_API(BaseProxyHandler);
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -120,17 +120,17 @@ enum JSGCTraceKind {
     JSTRACE_TYPE_OBJECT,
     JSTRACE_LAST = JSTRACE_TYPE_OBJECT
 };
 
 /* Struct forward declarations. */
 struct JSClass;
 struct JSCompartment;
 struct JSCrossCompartmentCall;
-struct JSErrorReport;
+class JSErrorReport;
 struct JSExceptionState;
 struct JSFunctionSpec;
 struct JSIdArray;
 struct JSLocaleCallbacks;
 struct JSObjectMap;
 struct JSPrincipals;
 struct JSPropertyDescriptor;
 struct JSPropertyName;
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -1085,16 +1085,45 @@ ScriptedDirectProxyHandler::construct(JS
 
 bool
 ScriptedDirectProxyHandler::isCallable(JSObject *obj) const
 {
     MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
     return obj->as<ProxyObject>().extra(IS_CALLABLE_EXTRA).toBoolean();
 }
 
+// ES6 implements both getPrototypeOf and setPrototypeOf traps. We don't have them yet (see bug
+// 888969). For now, use these, to account for proxy revocation.
+bool
+ScriptedDirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy,
+                                           MutableHandleObject protop) const
+{
+    RootedObject target(cx, proxy->as<ProxyObject>().target());
+    // Though handler is used elsewhere, spec mandates that both get set to null.
+    if (!target) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
+        return false;
+    }
+
+    return DirectProxyHandler::getPrototypeOf(cx, proxy, protop);
+}
+
+bool
+ScriptedDirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy,
+                                           HandleObject proto, bool *bp) const
+{
+    RootedObject target(cx, proxy->as<ProxyObject>().target());
+    if (!target) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
+        return false;
+    }
+
+    return DirectProxyHandler::setPrototypeOf(cx, proxy, proto, bp);
+}
+
 const char ScriptedDirectProxyHandler::family = 0;
 const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
 
 bool
 js::proxy(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 2) {
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -31,16 +31,21 @@ class ScriptedDirectProxyHandler : publi
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+    // These are standard internal methods, but are not implemented to spec yet.
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
+        const MOZ_OVERRIDE;
+    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
+        const MOZ_OVERRIDE;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE {
         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     }
 
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -12,17 +12,16 @@
 #include "vm/GlobalObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
-using mozilla::PodZero;
 
 /* static */ Shape *
 js::ErrorObject::assignInitialShape(ExclusiveContext *cx, Handle<ErrorObject*> obj)
 {
     MOZ_ASSERT(obj->empty());
 
     if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0))
         return nullptr;
@@ -111,17 +110,16 @@ JSErrorReport *
 js::ErrorObject::getOrCreateErrorReport(JSContext *cx)
 {
     if (JSErrorReport *r = getErrorReport())
         return r;
 
     // We build an error report on the stack and then use CopyErrorReport to do
     // the nitty-gritty malloc stuff.
     JSErrorReport report;
-    PodZero(&report);
 
     // Type.
     JSExnType type_ = type();
     report.exnType = type_;
 
     // Filename.
     JSAutoByteString filenameStr;
     if (!filenameStr.encodeLatin1(cx, fileName(cx)))
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -614,73 +614,57 @@ WrapCallable(JSContext *cx, HandleObject
 
     RootedValue priv(cx, ObjectValue(*callable));
     return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
                               priv, nullptr,
                               sandboxProtoProxy);
 }
 
 template<typename Op>
-bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id,
-                    unsigned attrFlag, HandleObject sandboxProtoProxy)
+bool WrapAccessorFunction(JSContext *cx, Op &op, JSPropertyDescriptor *desc,
+                          unsigned attrFlag, HandleObject sandboxProtoProxy)
 {
     if (!op) {
         return true;
     }
 
-    RootedObject func(cx);
-    if (desc->attrs & attrFlag) {
-        // Already an object
-        func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
-    } else {
-        // We have an actual property op.  For getters, we use 0
-        // args, for setters we use 1 arg.
-        uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
-        RootedObject obj(cx, desc->obj);
-        func = GeneratePropertyOp(cx, obj, id, args, op);
-        if (!func)
-            return false;
+    if (!(desc->attrs & attrFlag)) {
+        XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
+        return false;
     }
+
+    RootedObject func(cx, JS_FUNC_TO_DATA_PTR(JSObject *, op));
     func = WrapCallable(cx, func, sandboxProtoProxy);
     if (!func)
         return false;
     op = JS_DATA_TO_FUNC_PTR(Op, func.get());
-    desc->attrs |= attrFlag;
     return true;
 }
 
-extern bool
-XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
-extern bool
-XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp);
-
 bool
 xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
                                                 JS::Handle<JSObject*> proxy,
                                                 JS::Handle<jsid> id,
                                                 JS::MutableHandle<JSPropertyDescriptor> desc) const
 {
     JS::RootedObject obj(cx, wrappedObject(proxy));
 
     MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
     if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
         return false;
 
     if (!desc.object())
         return true; // No property, nothing to do
 
     // Now fix up the getter/setter/value as needed to be bound to desc->obj.
-    //
-    // Don't mess with XPC_WN_Helper_GetProperty and XPC_WN_Helper_SetProperty,
-    // because that could confuse our access to expandos.
-    if (desc.getter() != XPC_WN_Helper_GetProperty &&
-        !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy))
+    if (!WrapAccessorFunction(cx, desc.getter(), desc.address(),
+                              JSPROP_GETTER, proxy))
         return false;
-    if (desc.setter() != XPC_WN_Helper_SetProperty &&
-        !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy))
+    if (!WrapAccessorFunction(cx, desc.setter(), desc.address(),
+                              JSPROP_SETTER, proxy))
         return false;
     if (desc.value().isObject()) {
         RootedObject val (cx, &desc.value().toObject());
         if (JS::IsCallable(val)) {
             val = WrapCallable(cx, val, proxy);
             if (!val)
                 return false;
             desc.value().setObject(*val);
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -74,30 +74,16 @@ HasBitInInterfacesBitmap(JSObject *obj, 
 {
     MOZ_ASSERT(IS_WN_REFLECTOR(obj), "Not a wrapper?");
 
     const XPCWrappedNativeJSClass *clasp =
       (const XPCWrappedNativeJSClass*)js::GetObjectClass(obj);
     return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0;
 }
 
-static void
-PointerFinalize(JSFreeOp *fop, JSObject *obj)
-{
-    JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
-    delete popp;
-}
-
-const JSClass
-PointerHolderClass = {
-    "Pointer", JSCLASS_HAS_PRIVATE,
-    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
-};
-
 bool
 xpc_qsDefineQuickStubs(JSContext *cx, JSObject *protoArg, unsigned flags,
                        uint32_t ifacec, const nsIID **interfaces,
                        uint32_t tableSize, const xpc_qsHashEntry *table,
                        const xpc_qsPropertySpec *propspecs,
                        const xpc_qsFunctionSpec *funcspecs,
                        const char *stringTable)
 {
--- a/js/xpconnect/src/XPCQuickStubs.h
+++ b/js/xpconnect/src/XPCQuickStubs.h
@@ -524,92 +524,9 @@ xpc_qsSameResult(int32_t result1, int32_
     return result1 == result2;
 }
 
 #define XPC_QS_ASSERT_CONTEXT_OK(cx) xpc_qsAssertContextOK(cx)
 #else
 #define XPC_QS_ASSERT_CONTEXT_OK(cx) ((void) 0)
 #endif
 
-// Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
-template<typename Op>
-inline bool ApplyPropertyOp(JSContext *cx, Op op, JS::HandleObject obj, JS::HandleId id,
-                              JS::MutableHandleValue vp);
-
-template<>
-inline bool
-ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JS::HandleObject obj, JS::HandleId id,
-                              JS::MutableHandleValue vp)
-{
-    return op(cx, obj, id, vp);
-}
-
-template<>
-inline bool
-ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JS::HandleObject obj,
-                                    JS::HandleId id, JS::MutableHandleValue vp)
-{
-    return op(cx, obj, id, true, vp);
-}
-
-template<typename Op>
-bool
-PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
-{
-    // Layout:
-    //   this = our this
-    //   property op to call = callee reserved slot 0
-    //   name of the property = callee reserved slot 1
-
-    JS::CallArgs args = CallArgsFromVp(argc, vp);
-
-    JS::RootedObject callee(cx, &args.callee());
-    JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!obj)
-        return false;
-
-    JS::RootedValue v(cx, js::GetFunctionNativeReserved(callee, 0));
-
-    JSObject *ptrobj = v.toObjectOrNull();
-    Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
-
-    v = js::GetFunctionNativeReserved(callee, 1);
-
-    JS::RootedValue argval(cx, args.get(0));
-    JS::RootedId id(cx);
-    if (!JS_ValueToId(cx, v, &id))
-        return false;
-    args.rval().set(argval);
-    return ApplyPropertyOp<Op>(cx, *popp, obj, id, args.rval());
-}
-
-extern const JSClass PointerHolderClass;
-
-template<typename Op>
-JSObject *
-GeneratePropertyOp(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned argc, Op pop)
-{
-    // The JS engine provides two reserved slots on function objects for
-    // XPConnect to use. Use them to stick the necessary info here.
-    JSFunction *fun =
-        js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
-    if (!fun)
-        return nullptr;
-
-    JS::RootedObject funobj(cx, JS_GetFunctionObject(fun));
-
-    // Unfortunately, we cannot guarantee that Op is aligned. Use a
-    // second object to work around this.
-    JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, JS::NullPtr(), funobj);
-    if (!ptrobj)
-        return nullptr;
-    Op *popp = new Op;
-    if (!popp)
-        return nullptr;
-    *popp = pop;
-    JS_SetPrivate(ptrobj, popp);
-
-    js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
-    js::SetFunctionNativeReserved(funobj, 1, js::IdToValue(id));
-    return funobj;
-}
-
 #endif /* xpcquickstubs_h___ */
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -36,18 +36,16 @@ skip-if = buildapp == 'mulet'
 [test_bug726949.xul]
 skip-if = buildapp == 'mulet'
 [test_bug732665.xul]
 skip-if = buildapp == 'mulet'
 [test_bug738244.xul]
 [test_bug743843.xul]
 [test_bug760076.xul]
 skip-if = buildapp == 'mulet'
-[test_bug760109.xul]
-skip-if = buildapp == 'mulet'
 [test_bug760131.html]
 [test_bug763343.xul]
 [test_bug771429.xul]
 [test_bug773962.xul]
 [test_bug792280.xul]
 [test_bug793433.xul]
 [test_bug795275.xul]
 [test_bug799348.xul]
deleted file mode 100644
--- a/js/xpconnect/tests/chrome/test_bug760109.xul
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
-<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=760109
--->
-<window title="Mozilla Bug 760109"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <!-- test results are displayed in the html:body -->
-  <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=760109"
-     target="_blank">Mozilla Bug 760109</a>
-  </body>
-
-  <!-- test code goes here -->
-  <script type="application/javascript">
-  <![CDATA[
-
-  /** Test for COW prototype remapping.**/
-
-  // This gets decompiled and run inside the sandbox.
-  function sandboxCode() {
-
-    // Check that COWs for objects with standard prototypes use the standard
-    // prototype in the home compartment.
-    var protoProto = Object.getPrototypeOf(Object.getPrototypeOf(chromeObject));
-    ok(protoProto === Object.prototype,
-       "Object prototype remapped properly");
-
-    // Check |constructor|.
-    // Note that the 'constructor' property of the underlying chrome object
-    // will be resolved on SomeConstructor.prototype, which has an empty
-    // __exposedProps__. This means that we shouldn't remap the property, even
-    // though we'd also be able to find it on Object.prototype. Some recent
-    // refactoring has made it possible to do the right thing here.
-    is(typeof chromeObject.constructor, "undefined", "Object constructor does what we expect");
-  }
-
-  // We use a constructor to create the test object so that there's an
-  // intermediate object on the prototype chain between the instance and the
-  // standard prototype.
-  function SomeConstructor() {
-    this.foo = 2;
-    this.bar = 3;
-    this.baz = 4;
-    this.__exposedProps__ = {foo: 'r', baz: 'rw'};
-  }
-  SomeConstructor.prototype.__exposedProps__ = {};
-
-  const Cu = Components.utils;
-  var sb = new Cu.Sandbox('http://www.example.org');
-  sb.chromeObject = new SomeConstructor();
-  sb.ok = ok;
-  sb.is = is;
-  Cu.evalInSandbox('(' + sandboxCode.toSource() + ')();', sb);
-
-  ]]>
-  </script>
-</window>
--- a/js/xpconnect/tests/chrome/test_bug866823.xul
+++ b/js/xpconnect/tests/chrome/test_bug866823.xul
@@ -27,26 +27,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     // Make sure that Xray waivers can't be transitively extended onto objects
     // for whom the extender may not waive.
     var sb = new Cu.Sandbox(iwin, { wantXrays: true });
     sb.iwin = iwin;
     ok(Cu.evalInSandbox('iwin.wrappedJSObject.top == iwin.top', sb), "Waiver does not propagate");
     Cu.evalInSandbox('iwin.wrappedJSObject.waiver = iwin.wrappedJSObject', sb);
     ok(iwin.wrappedJSObject.eval('waiver == window'), "Waivers disappear same-compartment");
 
-    // Make sure that COW prototype always happens.
+    // Make sure that standard prototypes don't get COWs.
     sb.objProto = Object.prototype;
-    ok(Cu.evalInSandbox('objProto == Object.prototype', sb),
-                        "Prototype remapping should happen even when the object isn't used as a prototype");
-
-    // Make sure that prototype remapping happens even for nsEP.
-    var sbEP = new Cu.Sandbox(['http://example.org']);
-    sb.epObjProto = sbEP.Object.prototype;
-    ok(Cu.evalInSandbox('epObjProto == Object.prototype', sb),
-                        "Prototype remapping should happen for all non-subsuming access");
+    try {
+      sb.eval('objProto.toString;', sb);
+      ok(false, "Should have thrown");
+    } catch (e) {
+      ok(/denied|insecure/, "No silent failure when accessing properties on forbidden prototype");
+    }
 
     SimpleTest.finish();
   }
 
   ]]>
   </script>
   <iframe id="ifr" onload="go();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
 </window>
--- a/js/xpconnect/tests/unit/test_bug1081990.js
+++ b/js/xpconnect/tests/unit/test_bug1081990.js
@@ -1,10 +1,10 @@
 const Cu = Components.utils;
 function run_test() {
   var sb = new Cu.Sandbox('http://www.example.com');
   sb.obj = {};
   sb.arr = [];
   sb.fun = function() {};
-  do_check_true(sb.eval('Object.getPrototypeOf(obj) == Object.prototype'));
+  do_check_true(sb.eval('Object.getPrototypeOf(obj) == null'));
   do_check_true(sb.eval('Object.getPrototypeOf(arr) == null'));
   do_check_true(sb.eval('Object.getPrototypeOf(fun) == null'));
 }
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -3,16 +3,17 @@
 /* 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 "AccessCheck.h"
 
 #include "nsJSPrincipals.h"
 #include "nsGlobalWindow.h"
+#include "JavaScriptParent.h"
 
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/LocationBinding.h"
 #include "mozilla/dom/WindowBinding.h"
@@ -208,16 +209,73 @@ AccessCheck::isCrossOriginAccessPermitte
                 return false;
             }
         }
         return IsFrameId(cx, obj, id);
     }
     return false;
 }
 
+bool
+AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, HandleValue v)
+{
+    // Primitives are fine.
+    if (!v.isObject())
+        return true;
+    RootedObject obj(cx, &v.toObject());
+
+    // Non-wrappers are fine.
+    if (!js::IsWrapper(obj))
+        return true;
+
+    // CPOWs use COWs (in the unprivileged junk scope) for all child->parent
+    // references. Without this test, the child process wouldn't be able to
+    // pass any objects at all to CPOWs.
+    if (mozilla::jsipc::IsWrappedCPOW(obj) &&
+        js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
+        XRE_GetProcessType() == GeckoProcessType_Default)
+    {
+        return true;
+    }
+
+    // COWs are fine to pass to chrome if and only if they have __exposedProps__,
+    // since presumably content should never have a reason to pass an opaque
+    // object back to chrome.
+    if (AccessCheck::isChrome(js::UncheckedUnwrap(wrapper)) && WrapperFactory::IsCOW(obj)) {
+        RootedObject target(cx, js::UncheckedUnwrap(obj));
+        JSAutoCompartment ac(cx, target);
+        RootedId id(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS));
+        bool found = false;
+        if (!JS_HasPropertyById(cx, target, id, &found))
+            return false;
+        if (found)
+            return true;
+    }
+
+    // Same-origin wrappers are fine.
+    if (AccessCheck::wrapperSubsumes(obj))
+        return true;
+
+    // Badness.
+    JS_ReportError(cx, "Permission denied to pass object to privileged code");
+    return false;
+}
+
+bool
+AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, const CallArgs &args)
+{
+    if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv()))
+        return false;
+    for (size_t i = 0; i < args.length(); ++i) {
+        if (!checkPassToPrivilegedCode(cx, wrapper, args[i]))
+            return false;
+    }
+    return true;
+}
+
 enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
 
 static void
 EnterAndThrow(JSContext *cx, JSObject *wrapper, const char *msg)
 {
     JSAutoCompartment ac(cx, wrapper);
     JS_ReportError(cx, msg);
 }
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -34,18 +34,16 @@ class AccessCheck {
 enum CrossOriginObjectType {
     CrossOriginWindow,
     CrossOriginLocation,
     CrossOriginOpaque
 };
 CrossOriginObjectType IdentifyCrossOriginObject(JSObject *obj);
 
 struct Policy {
-    static const bool AllowGetPrototypeOf = false;
-
     static bool checkCall(JSContext *cx, JS::HandleObject wrapper, const JS::CallArgs &args) {
         MOZ_CRASH("As a rule, filtering wrappers are non-callable");
     }
 };
 
 // This policy allows no interaction with the underlying callable. Everything throws.
 struct Opaque : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
@@ -90,22 +88,16 @@ struct CrossOriginAccessiblePropertiesOn
     static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
         return false;
     }
 };
 
 // This policy only permits access to properties if they appear in the
 // objects exposed properties list.
 struct ExposedPropertiesOnly : public Policy {
-
-    // COWs are the only type of filtering wrapper that allow access to the
-    // prototype, because the standard prototypes are remapped into the
-    // wrapper's compartment.
-    static const bool AllowGetPrototypeOf = true;
-
     static bool check(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id, js::Wrapper::Action act);
 
     static bool deny(js::Wrapper::Action act, JS::HandleId id);
     static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
         return false;
     }
 };
 
--- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
+++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
@@ -2,172 +2,27 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* 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 "ChromeObjectWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
-#include "JavaScriptParent.h"
 #include "xpcprivate.h"
 #include "jsapi.h"
 #include "jswrapper.h"
 #include "nsXULAppAPI.h"
 
 using namespace JS;
 
 namespace xpc {
 
-// When creating wrappers for chrome objects in content, we detect if the
-// prototype of the wrapped chrome object is a prototype for a standard class
-// (like Array.prototype). If it is, we use the corresponding standard prototype
-// from the wrapper's scope, rather than the wrapped standard prototype
-// from the wrappee's scope.
-//
-// One of the reasons for doing this is to allow standard operations like
-// chromeArray.forEach(..) to Just Work without explicitly listing them in
-// __exposedProps__. Since proxies don't automatically inherit behavior from
-// their prototype, we have to instrument the traps to do this manually.
 const ChromeObjectWrapper ChromeObjectWrapper::singleton;
 
-using js::assertEnteredPolicy;
-
-static bool
-AllowedByBase(JSContext *cx, HandleObject wrapper, HandleId id,
-              js::Wrapper::Action act)
-{
-    MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
-               &ChromeObjectWrapper::singleton);
-    bool bp;
-    const ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
-    return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp);
-}
-
-static bool
-PropIsFromStandardPrototype(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
-{
-    MOZ_ASSERT(desc.object());
-    RootedObject unwrapped(cx, js::UncheckedUnwrap(desc.object()));
-    JSAutoCompartment ac(cx, unwrapped);
-    return IdentifyStandardPrototype(unwrapped) != JSProto_Null;
-}
-
-// Note that we're past the policy enforcement stage, here, so we can query
-// CrossCompartmentSecurityWrapper (our grand-parent wrapper) and get an
-// unfiltered view of the underlying object. This lets us determine whether
-// the property we would have found (given a transparent wrapper) would
-// have come off a standard prototype.
-static bool
-PropIsFromStandardPrototype(JSContext *cx, HandleObject wrapper,
-                            HandleId id)
-{
-    MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
-               &ChromeObjectWrapper::singleton);
-    Rooted<JSPropertyDescriptor> desc(cx);
-    const ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
-    if (!handler->js::CrossCompartmentSecurityWrapper::getPropertyDescriptor(cx, wrapper, id,
-                                                                             &desc) ||
-        !desc.object())
-    {
-        return false;
-    }
-    return PropIsFromStandardPrototype(cx, &desc);
-}
-
-bool
-ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx,
-                                           HandleObject wrapper,
-                                           HandleId id,
-                                           JS::MutableHandle<JSPropertyDescriptor> desc) const
-{
-    assertEnteredPolicy(cx, wrapper, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
-    // First, try a lookup on the base wrapper if permitted.
-    desc.object().set(nullptr);
-    if (AllowedByBase(cx, wrapper, id, Wrapper::GET) &&
-        !ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id,
-                                                        desc)) {
-        return false;
-    }
-
-    // If the property is something that can be found on a standard prototype,
-    // prefer the one we'll get via the prototype chain in the content
-    // compartment.
-    if (desc.object() && PropIsFromStandardPrototype(cx, desc))
-        desc.object().set(nullptr);
-
-    // If we found something or have no proto, we're done.
-    RootedObject wrapperProto(cx);
-    if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
-      return false;
-    if (desc.object() || !wrapperProto)
-        return true;
-
-    // If not, try doing the lookup on the prototype.
-    MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
-    return JS_GetPropertyDescriptorById(cx, wrapperProto, id, desc);
-}
-
-bool
-AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, HandleValue v)
-{
-    // Primitives are fine.
-    if (!v.isObject())
-        return true;
-    RootedObject obj(cx, &v.toObject());
-
-    // Non-wrappers are fine.
-    if (!js::IsWrapper(obj))
-        return true;
-
-    // CPOWs use COWs (in the unprivileged junk scope) for all child->parent
-    // references. Without this test, the child process wouldn't be able to
-    // pass any objects at all to CPOWs.
-    if (mozilla::jsipc::IsWrappedCPOW(obj) &&
-        js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
-        XRE_GetProcessType() == GeckoProcessType_Default)
-    {
-        return true;
-    }
-
-    // COWs are fine to pass to chrome if and only if they have __exposedProps__,
-    // since presumably content should never have a reason to pass an opaque
-    // object back to chrome.
-    if (AccessCheck::isChrome(js::UncheckedUnwrap(wrapper)) && WrapperFactory::IsCOW(obj)) {
-        RootedObject target(cx, js::UncheckedUnwrap(obj));
-        JSAutoCompartment ac(cx, target);
-        RootedId id(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS));
-        bool found = false;
-        if (!JS_HasPropertyById(cx, target, id, &found))
-            return false;
-        if (found)
-            return true;
-    }
-
-    // Same-origin wrappers are fine.
-    if (AccessCheck::wrapperSubsumes(obj))
-        return true;
-
-    // Badness.
-    JS_ReportError(cx, "Permission denied to pass object to privileged code");
-    return false;
-}
-
-bool
-AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, const CallArgs &args)
-{
-    if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv()))
-        return false;
-    for (size_t i = 0; i < args.length(); ++i) {
-        if (!checkPassToPrivilegedCode(cx, wrapper, args[i]))
-            return false;
-    }
-    return true;
-}
-
 bool
 ChromeObjectWrapper::defineProperty(JSContext *cx, HandleObject wrapper,
                                     HandleId id,
                                     MutableHandle<JSPropertyDescriptor> desc) const
 {
     if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, desc.value()))
         return false;
     return ChromeObjectWrapperBase::defineProperty(cx, wrapper, id, desc);
@@ -178,105 +33,9 @@ ChromeObjectWrapper::set(JSContext *cx, 
                          HandleObject receiver, HandleId id,
                          bool strict, MutableHandleValue vp) const
 {
     if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, vp))
         return false;
     return ChromeObjectWrapperBase::set(cx, wrapper, receiver, id, strict, vp);
 }
 
-
-
-bool
-ChromeObjectWrapper::has(JSContext *cx, HandleObject wrapper,
-                         HandleId id, bool *bp) const
-{
-    assertEnteredPolicy(cx, wrapper, id, GET);
-    // Try the lookup on the base wrapper if permitted.
-    if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
-        !ChromeObjectWrapperBase::has(cx, wrapper, id, bp))
-    {
-        return false;
-    }
-
-    // If we found something or have no prototype, we're done.
-    RootedObject wrapperProto(cx);
-    if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
-        return false;
-    if (*bp || !wrapperProto)
-        return true;
-
-    // Try the prototype if that failed.
-    MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
-    Rooted<JSPropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, wrapperProto, id, &desc))
-        return false;
-    *bp = !!desc.object();
-    return true;
 }
-
-bool
-ChromeObjectWrapper::get(JSContext *cx, HandleObject wrapper,
-                         HandleObject receiver, HandleId id,
-                         MutableHandleValue vp) const
-{
-    assertEnteredPolicy(cx, wrapper, id, GET);
-    vp.setUndefined();
-    // Only call through to the get trap on the underlying object if we're
-    // allowed to see the property, and if what we'll find is not on a standard
-    // prototype.
-    if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
-        !PropIsFromStandardPrototype(cx, wrapper, id))
-    {
-        // Call the get trap.
-        if (!ChromeObjectWrapperBase::get(cx, wrapper, receiver, id, vp))
-            return false;
-        // If we found something, we're done.
-        if (!vp.isUndefined())
-            return true;
-    }
-
-    // If we have no proto, we're done.
-    RootedObject wrapperProto(cx);
-    if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
-        return false;
-    if (!wrapperProto)
-        return true;
-
-    // Try the prototype.
-    MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
-    return js::GetGeneric(cx, wrapperProto, receiver, id, vp.address());
-}
-
-// SecurityWrapper categorically returns false for objectClassIs, but the
-// contacts API depends on Array.isArray returning true for COW-implemented
-// contacts. This isn't really ideal, but make it work for now.
-bool
-ChromeObjectWrapper::objectClassIs(HandleObject obj, js::ESClassValue classValue,
-                                   JSContext *cx) const
-{
-  return CrossCompartmentWrapper::objectClassIs(obj, classValue, cx);
-}
-
-// This mechanism isn't ideal because we end up calling enter() on the base class
-// twice (once during enter() here and once during the trap itself), and policy
-// enforcement or COWs isn't cheap. But it results in the cleanest code, and this
-// whole proto remapping thing for COWs is going to be phased out anyway.
-bool
-ChromeObjectWrapper::enter(JSContext *cx, HandleObject wrapper,
-                           HandleId id, js::Wrapper::Action act, bool *bp) const
-{
-    if (AllowedByBase(cx, wrapper, id, act))
-        return true;
-    // COWs fail silently for GETs, and that also happens to be the only case
-    // where we might want to redirect the lookup to the home prototype chain.
-    *bp = (act == Wrapper::GET || act == Wrapper::ENUMERATE ||
-           act == Wrapper::GET_PROPERTY_DESCRIPTOR) && !JS_IsExceptionPending(cx);
-    if (!*bp || id == JSID_VOID)
-        return false;
-
-    // Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor
-    // before we've fully entered the policy. Waive our policy.
-    js::AutoWaivePolicy policy(cx, wrapper, id, act);
-    return PropIsFromStandardPrototype(cx, wrapper, id);
-}
-
-}
--- a/js/xpconnect/wrappers/ChromeObjectWrapper.h
+++ b/js/xpconnect/wrappers/ChromeObjectWrapper.h
@@ -10,56 +10,33 @@
 #include "mozilla/Attributes.h"
 
 #include "FilteringWrapper.h"
 
 namespace xpc {
 
 struct ExposedPropertiesOnly;
 
-// When chrome JS objects are exposed to content, they get a ChromeObjectWrapper.
-//
-// The base filtering wrapper here does most of the work for us. We define a
-// custom class here to introduce custom behavior with respect to the prototype
-// chain.
+// When a vanilla chrome JS object is exposed to content, we use a wrapper that
+// supports __exposedProps__ for legacy reasons. For extra security, we override
+// the traps that allow content to pass an object to chrome, and perform extra
+// security checks on them.
 #define ChromeObjectWrapperBase \
   FilteringWrapper<js::CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>
 
 class ChromeObjectWrapper : public ChromeObjectWrapperBase
 {
   public:
     MOZ_CONSTEXPR ChromeObjectWrapper() : ChromeObjectWrapperBase(0) {}
 
-    virtual bool enter(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
-                       js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE;
-
     virtual bool defineProperty(JSContext *cx, JS::Handle<JSObject*> wrapper,
                                 JS::Handle<jsid> id,
                                 JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
-    virtual bool has(JSContext *cx, JS::Handle<JSObject*> wrapper,
-                     JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
-    virtual bool get(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
-                     JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, JS::Handle<JSObject*> wrapper,
                      JS::Handle<JSObject*> receiver, JS::Handle<jsid> id,
                      bool strict, JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
 
-    virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper,
-                                       JS::Handle<jsid> id,
-                                       JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
-    virtual bool objectClassIs(JS::Handle<JSObject*> obj, js::ESClassValue classValue,
-                               JSContext *cx) const MOZ_OVERRIDE;
-
-    // NB: One might think we'd need to implement enumerate(),
-    // getOwnEnumerablePropertyKeys(), iterate(), and ownPropertyKeys()
-    // here. However, ES5 built-in properties aren't enumerable (and
-    // SpiderMonkey's implementation seems to match the spec modulo
-    // Error.prototype.fileName and Error.prototype.lineNumber). Since we're
-    // only remapping the prototypes of standard objects, there would never be
-    // anything more to enumerate up the prototype chain. So we can actually
-    // skip these.
-
     static const ChromeObjectWrapper singleton;
 };
 
 } /* namespace xpc */
 
 #endif /* __ChromeObjectWrapper_h__ */
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -172,21 +172,17 @@ FilteringWrapper<Base, Policy>::defaultV
     return Base::defaultValue(cx, obj, hint, vp);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
                                                JS::MutableHandleObject protop) const
 {
-    // If the policy explicitly allows access to the prototype, bounce to the base.
-    if (Policy::AllowGetPrototypeOf)
-        return Base::getPrototypeOf(cx, wrapper, protop);
-
-    // In general, filtering wrappers do not allow access to the prototype.
+    // Filtering wrappers do not allow access to the prototype.
     protop.set(nullptr);
     return true;
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::enter(JSContext *cx, HandleObject wrapper,
                                       HandleId id, Wrapper::Action act, bool *bp) const
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -102,25 +102,16 @@ WrapperFactory::WaiveXray(JSContext *cx,
     MOZ_ASSERT(!js::IsInnerObject(obj));
 
     JSObject *waiver = GetXrayWaiver(obj);
     if (waiver)
         return waiver;
     return CreateXrayWaiver(cx, obj);
 }
 
-// In general, we're trying to deprecate COWs incrementally as we introduce
-// Xrays to the corresponding object types. But switching off COWs for certain
-// things would be too tumultuous at present, so we punt on them for later.
-static bool
-ForceCOWBehavior(JSObject *obj)
-{
-    return IdentifyStandardInstanceOrPrototype(obj) == JSProto_Object;
-}
-
 inline bool
 ShouldWaiveXray(JSContext *cx, JSObject *originalObj)
 {
     unsigned flags;
     (void) js::UncheckedUnwrap(originalObj, /* stopAtOuter = */ true, &flags);
 
     // If the original object did not point through an Xray waiver, we're done.
     if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG))
@@ -166,55 +157,16 @@ WrapperFactory::PrepareForWrapping(JSCon
     // this case first to allow us to assert against wrappers below.
     if (js::IsOuterObject(obj))
         return waive ? WaiveXray(cx, obj) : obj;
 
     // Here are the rules for wrapping:
     // We should never get a proxy here (the JS engine unwraps those for us).
     MOZ_ASSERT(!IsWrapper(obj));
 
-    // If the object being wrapped is a prototype for a standard class and the
-    // wrapper does not subsumes the wrappee, use the one from the content
-    // compartment. This is generally safer all-around, and in the COW case this
-    // lets us safely take advantage of things like .forEach() via the
-    // ChromeObjectWrapper machinery.
-    //
-    // If the prototype chain of chrome object |obj| looks like this:
-    //
-    // obj => foo => bar => chromeWin.StandardClass.prototype
-    //
-    // The prototype chain of COW(obj) looks lke this:
-    //
-    // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype
-    //
-    // NB: We now remap all non-subsuming access of standard prototypes.
-    //
-    // NB: We need to ignore domain here so that the security relationship we
-    // compute here can't change over time. See the comment above the other
-    // subsumes call below.
-    bool subsumes = AccessCheck::subsumes(js::GetContextCompartment(cx),
-                                          js::GetObjectCompartment(obj));
-    XrayType xrayType = GetXrayType(obj);
-    if (!subsumes && (xrayType == NotXray || ForceCOWBehavior(obj))) {
-        JSProtoKey key = JSProto_Null;
-        {
-            JSAutoCompartment ac(cx, obj);
-            key = IdentifyStandardPrototype(obj);
-        }
-        if (key != JSProto_Null) {
-            RootedObject homeProto(cx);
-            if (!JS_GetClassPrototype(cx, key, &homeProto))
-                return nullptr;
-            MOZ_ASSERT(homeProto);
-            // No need to double-wrap here. We should never have waivers to
-            // COWs.
-            return homeProto;
-        }
-    }
-
     // Now, our object is ready to be wrapped, but several objects (notably
     // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
     // those objects in a security wrapper, then we need to hand back the
     // wrapper for the new scope instead. Also, global objects don't move
     // between scopes so for those we also want to return the wrapper. So...
     if (!IS_WN_REFLECTOR(obj) || !js::GetObjectParent(obj))
         return waive ? WaiveXray(cx, obj) : obj;
 
@@ -473,23 +425,21 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     // If this is a chrome function being exposed to content, we need to allow
     // call (but nothing else).
     else if (originIsChrome && !targetIsChrome &&
              IdentifyStandardInstance(obj) == JSProto_Function)
     {
         wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
     }
 
-    // If this is a chrome object being exposed to content without Xrays, use
-    // a COW.
-    //
-    // We make an exception for Object instances, because we still rely on COWs
-    // for those in a lot of places in the tree.
+    // For Vanilla JSObjects exposed from chrome to content, we use a wrapper
+    // that supports __exposedProps__. We'd like to get rid of these eventually,
+    // but in their current form they don't cause much trouble.
     else if (originIsChrome && !targetIsChrome &&
-             (xrayType == NotXray || ForceCOWBehavior(obj)))
+             IdentifyStandardInstance(obj) == JSProto_Object)
     {
         wrapper = &ChromeObjectWrapper::singleton;
     }
 
     //
     // Now, handle the regular cases.
     //
     // These are wrappers we can compute using a rule-based approach. In order
--- a/layout/base/nsPresArena.h
+++ b/layout/base/nsPresArena.h
@@ -29,16 +29,19 @@ public:
   enum ObjectID {
     nsLineBox_id = nsQueryFrame::NON_FRAME_MARKER,
     nsRuleNode_id,
     nsStyleContext_id,
     nsInheritedStyleData_id,
     nsResetStyleData_id,
     nsFrameList_id,
 
+    CustomCounterStyle_id,
+    DependentBuiltinCounterStyle_id,
+
     First_nsStyleStruct_id,
     DummyBeforeStyleStructs_id = First_nsStyleStruct_id - 1,
 
     #define STYLE_STRUCT(name_, checkdata_cb_) \
       nsStyle##name_##_id,
     #include "nsStyleStructList.h"
     #undef STYLE_STRUCT
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -963,21 +963,16 @@ nsPresContext::Init(nsDeviceContext* aDe
   mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
 
   mEventManager = new mozilla::EventStateManager();
 
   mTransitionManager = new nsTransitionManager(this);
 
   mAnimationManager = new nsAnimationManager(this);
 
-  // Since CounterStyleManager is also the name of a method of
-  // nsPresContext, it is necessary to prefix the class with the mozilla
-  // namespace here.
-  mCounterStyleManager = new mozilla::CounterStyleManager(this);
-
   if (mDocument->GetDisplayDocument()) {
     NS_ASSERTION(mDocument->GetDisplayDocument()->GetShell() &&
                  mDocument->GetDisplayDocument()->GetShell()->GetPresContext(),
                  "Why are we being initialized?");
     mRefreshDriver = mDocument->GetDisplayDocument()->GetShell()->
       GetPresContext()->RefreshDriver();
   } else {
     nsIDocument* parent = mDocument->GetParentDocument();
@@ -1095,29 +1090,38 @@ nsPresContext::Init(nsDeviceContext* aDe
 void
 nsPresContext::SetShell(nsIPresShell* aShell)
 {
   if (mFontFaceSet) {
     // Clear out user font set if we have one
     mFontFaceSet->DestroyUserFontSet();
     mFontFaceSet = nullptr;
   }
+  if (mCounterStyleManager) {
+    mCounterStyleManager->Disconnect();
+    mCounterStyleManager = nullptr;
+  }
 
   if (mShell) {
     // Remove ourselves as the charset observer from the shell's doc, because
     // this shell may be going away for good.
     nsIDocument *doc = mShell->GetDocument();
     if (doc) {
       doc->RemoveCharSetObserver(this);
     }
   }
 
   mShell = aShell;
 
   if (mShell) {
+    // Since CounterStyleManager is also the name of a method of
+    // nsPresContext, it is necessary to prefix the class with the mozilla
+    // namespace here.
+    mCounterStyleManager = new mozilla::CounterStyleManager(this);
+
     nsIDocument *doc = mShell->GetDocument();
     NS_ASSERTION(doc, "expect document here");
     if (doc) {
       // Have to update PresContext's mDocument before calling any other methods.
       mDocument = doc;
     }
     // Initialize our state from the user preferences, now that we
     // have a presshell, and hence a document.
@@ -1151,20 +1155,16 @@ nsPresContext::SetShell(nsIPresShell* aS
     if (mAnimationManager) {
       mAnimationManager->Disconnect();
       mAnimationManager = nullptr;
     }
     if (mRestyleManager) {
       mRestyleManager->Disconnect();
       mRestyleManager = nullptr;
     }
-    if (mCounterStyleManager) {
-      mCounterStyleManager->Disconnect();
-      mCounterStyleManager = nullptr;
-    }
 
     if (IsRoot()) {
       // Have to cancel our plugin geometry timer, because the
       // callback for that depends on a non-null presshell.
       static_cast<nsRootPresContext*>(this)->CancelApplyPluginGeometryTimer();
     }
   }
 }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-blending/mix-blend-mode-and-filter-ref.html
@@ -0,0 +1,41 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <style>
+    .parent {
+      position: relative;
+      width: 100px;
+      height: 100px;
+    }
+
+    .child {
+      filter: blur(3px);
+      background: #0f0;
+      width: 100px;
+      height: 100px;
+    }
+
+    .black-rect {
+      position: absolute;
+      top: 50px;
+      width: 100px;
+      height: 50px;
+      background: #000;
+    }
+  </style>
+</head>
+<body>
+  <!--
+    You should see a blurred green square, with its bottom half mostly
+    covered by a black rectangle.
+  -->
+  <div class="parent">
+    <div class="child"></div>
+    <div class="black-rect"></div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-blending/mix-blend-mode-and-filter-ref.svg
@@ -0,0 +1,14 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <filter id="blur">
+      <feGaussianBlur stdDeviation="3"/>
+    </filter>
+  </defs>
+  <rect x="0" y="0" width="100" height="50" fill="#fff"/>
+  <rect x="0" y="0" width="100" height="100" fill="#0f0" filter="url(#blur)"/>
+  <rect x="0" y="50" width="100" height="50" fill="#000"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-blending/mix-blend-mode-and-filter.html
@@ -0,0 +1,37 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <style>
+    .parent {
+      /*
+        With the darken blend mode, green will overwrite white parts of this
+        background, while black parts of this background will show through.
+      */
+      background: linear-gradient(#fff 0%, #fff 50%, #000 50%, #000 100%);
+      width: 100px;
+      height: 100px;
+    }
+
+    .child {
+      filter: blur(3px);
+      mix-blend-mode: darken;
+      background: #0f0;
+      width: 100px;
+      height: 100px;
+    }
+  </style>
+</head>
+<body>
+  <!--
+    You should see a blurred green square, with its bottom half mostly
+    covered by a black rectangle.
+  -->
+  <div class="parent">
+    <div class="child"></div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-blending/mix-blend-mode-and-filter.svg
@@ -0,0 +1,21 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <filter id="blur">
+      <feGaussianBlur stdDeviation="3"/>
+    </filter>
+  </defs>
+  <!-- Background consists of a white rect and a black rect. -->
+  <rect x="0" y="0" width="100" height="50" fill="#fff"/>
+  <rect x="0" y="50" width="100" height="50" fill="#000"/>
+  <!--
+    Foreground consists of a blurred green square, blended with the background
+    using the darken blend mode. Green should overwrite the white rect, while
+    the black rect should show through.
+  -->
+  <rect x="0" y="0" width="100" height="100" fill="#0f0" filter="url(#blur)"
+        style="mix-blend-mode: darken;"/>
+</svg>
--- a/layout/reftests/css-blending/reftest.list
+++ b/layout/reftests/css-blending/reftest.list
@@ -35,16 +35,19 @@ fuzzy-if(d2d,1,1600) fuzzy-if(azureSkia|
 fuzzy-if(d2d||azureSkia||gtk2Widget,1,1600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-screen.html background-blending-screen-ref.svg
 fuzzy-if(azureQuartz,1,1600) fuzzy-if(d2d||azureSkia||gtk2Widget,10,4800) pref(layout.css.background-blend-mode.enabled,true) == background-blending-soft-light.html background-blending-soft-light-ref.svg
 
 fuzzy-if(azureQuartz,2,40000) fuzzy-if(azureSkia||d2d||gtk2Widget,1,40000) pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-959674.html background-blending-image-color-959674-ref.html
 
 #fuzzy due to inconsistencies in rounded rect cliping between parent and child; may be related to antialiasing. Between platforms, the max difference is the same, and the number of different pixels is either 36 or 37. (Win, Mac and Lin)
 fuzzy(64,37) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-952051.html mix-blend-mode-952051-ref.html
 
+pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.html mix-blend-mode-and-filter-ref.html
+pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.svg mix-blend-mode-and-filter-ref.svg
+
 pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html
 
 pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-nested-976533.html mix-blend-mode-nested-976533-ref.html
 
 # Test plan 5.3.1 Blending between the background layers and the background color for an element with background-blend-mode
 # Test 9
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-svg-as-data-uri.html background-blending-image-color-ref.html
 # Test 10
--- a/layout/reftests/ogg-video/reftest.list
+++ b/layout/reftests/ogg-video/reftest.list
@@ -1,18 +1,19 @@
-fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html # bug 773482
-fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html # bug 773482
-fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html # bug 773482
-fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html # bug 773482
+# NOTE: bug 1084564 covers "fails"/"skip" annotations for b2g/android below:
+fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html
+fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html
+fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html
+fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html
 HTTP(..) == aspect-ratio-3a.xhtml aspect-ratio-3-ref.xhtml
 HTTP(..) == aspect-ratio-3b.xhtml aspect-ratio-3-ref.xhtml
-fails-if(Android||B2G) random-if(layersGPUAccelerated) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu))  == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html # bug 623460 for WinXP # bug 773482
-fails-if(Android||B2G) HTTP(..) == basic-1.xhtml basic-1-ref.html # bug 773482
-skip-if(Android||B2G) HTTP(..) == canvas-1a.xhtml basic-1-ref.html # bug 773482
-fails-if(Android||B2G) HTTP(..) == canvas-1b.xhtml basic-1-ref.html # bug 773482
+fails-if(Android||B2G) random-if(layersGPUAccelerated) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu))  == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html # bug 623460 for WinXP
+fails-if(Android||B2G) HTTP(..) == basic-1.xhtml basic-1-ref.html
+skip-if(Android||B2G) HTTP(..) == canvas-1a.xhtml basic-1-ref.html
+fails-if(Android||B2G) HTTP(..) == canvas-1b.xhtml basic-1-ref.html
 == clipping-1a.html clipping-1-ref.html
 == empty-1a.html empty-1-ref.html
 == empty-1b.html empty-1-ref.html
 #these is skipped because we hang on the htmlparser tests when this is ran
 random skip-if(Android||B2G) HTTP(..) == object-aspect-ratio-1a.xhtml aspect-ratio-1-ref.html
 random skip-if(Android||B2G) HTTP(..) == object-aspect-ratio-1b.xhtml aspect-ratio-1-ref.html
 skip-if(Android||B2G) HTTP(..) == offset-1.xhtml offset-1-ref.html
 random skip-if(Android||B2G) HTTP(..) == object-aspect-ratio-2a.xhtml aspect-ratio-2-ref.html
--- a/layout/reftests/webm-video/reftest.list
+++ b/layout/reftests/webm-video/reftest.list
@@ -1,8 +1,9 @@
+# NOTE: bug 1084564 covers "fails"/"skip" annotations for b2g/android below:
 fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html
 fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html
 fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html
 fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html
 HTTP(..) == aspect-ratio-3a.xhtml aspect-ratio-3-ref.xhtml
 HTTP(..) == aspect-ratio-3b.xhtml aspect-ratio-3-ref.xhtml
 fails-if(Android||B2G) random-if(layersGPUAccelerated) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu))  == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html # bug 623460 for WinXP
 fails-if(Android||B2G) HTTP(..) == basic-1.xhtml basic-1-ref.html
--- a/layout/style/CounterStyleManager.cpp
+++ b/layout/style/CounterStyleManager.cpp
@@ -961,22 +961,42 @@ public:
     NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style");
     NS_ABORT_IF_FALSE(!IsCustomStyle(), "Not a builtin style");
   }
 
   virtual CounterStyle* GetFallback() MOZ_OVERRIDE;
 
   // DependentBuiltinCounterStyle is managed in the same way as
   // CustomCounterStyle.
-  NS_INLINE_DECL_REFCOUNTING(DependentBuiltinCounterStyle)
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() MOZ_OVERRIDE;
+  NS_IMETHOD_(MozExternalRefCountType) Release() MOZ_OVERRIDE;
+
+  void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+  {
+    return aPresContext->PresShell()->AllocateByObjectID(
+        nsPresArena::DependentBuiltinCounterStyle_id, sz);
+  }
 
 private:
+  void Destroy()
+  {
+    nsIPresShell* shell = mManager->PresContext()->PresShell();
+    this->~DependentBuiltinCounterStyle();
+    shell->FreeByObjectID(nsPresArena::DependentBuiltinCounterStyle_id, this);
+  }
+
   CounterStyleManager* mManager;
+
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
 };
 
+NS_IMPL_ADDREF(DependentBuiltinCounterStyle)
+NS_IMPL_RELEASE_WITH_DESTROY(DependentBuiltinCounterStyle, Destroy())
+
 /* virtual */ CounterStyle*
 DependentBuiltinCounterStyle::GetFallback()
 {
   switch (GetStyle()) {
     case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
     case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
     case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
     case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
@@ -1059,19 +1079,33 @@ public:
   bool IsExtendsSystem()
   {
     return mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS;
   }
 
   // CustomCounterStyle should be reference-counted because it may be
   // dereferenced from the manager but still referenced by nodes and
   // frames before the style change is propagated.
-  NS_INLINE_DECL_REFCOUNTING(CustomCounterStyle)
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() MOZ_OVERRIDE;
+  NS_IMETHOD_(MozExternalRefCountType) Release() MOZ_OVERRIDE;
+
+  void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
+  {
+    return aPresContext->PresShell()->AllocateByObjectID(
+        nsPresArena::CustomCounterStyle_id, sz);
+  }
 
 private:
+  void Destroy()
+  {
+    nsIPresShell* shell = mManager->PresContext()->PresShell();
+    this->~CustomCounterStyle();
+    shell->FreeByObjectID(nsPresArena::CustomCounterStyle_id, this);
+  }
+
   const nsTArray<nsString>& GetSymbols();
   const nsTArray<AdditiveSymbol>& GetAdditiveSymbols();
 
   // The speak-as values of counter styles may form a loop, and the
   // loops may have complex interaction with the loop formed by
   // extending. To solve this problem, the computation of speak-as is
   // divided into two phases:
   // 1. figure out the raw value, by ComputeRawSpeakAs, and
@@ -1134,18 +1168,24 @@ private:
   // That counter must not speak as another counter.
   CounterStyle* mSpeakAsCounter;
 
   CounterStyle* mExtends;
   // This field refers to the last counter in the extends chain. The
   // counter must be either a builtin style or a style whose system is
   // not 'extends'.
   CounterStyle* mExtendsRoot;
+
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
 };
 
+NS_IMPL_ADDREF(CustomCounterStyle)
+NS_IMPL_RELEASE_WITH_DESTROY(CustomCounterStyle, Destroy())
+
 void
 CustomCounterStyle::ResetCachedData()
 {
   mSymbols.Clear();
   mAdditiveSymbols.Clear();
   mFlags &= ~(FLAG_NEGATIVE_INITED |
               FLAG_PREFIX_INITED |
               FLAG_SUFFIX_INITED |
@@ -1963,23 +2003,23 @@ CounterStyleManager::BuildCounterStyle(c
     return data;
   }
 
   // It is intentional that the predefined names are case-insensitive
   // but the user-defined names case-sensitive.
   nsCSSCounterStyleRule* rule =
     mPresContext->StyleSet()->CounterStyleRuleForName(mPresContext, aName);
   if (rule) {
-    data = new CustomCounterStyle(this, rule);
+    data = new (mPresContext) CustomCounterStyle(this, rule);
   } else {
     int32_t type;
     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aName);
     if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, type)) {
       if (gBuiltinStyleTable[type].IsDependentStyle()) {
-        data = new DependentBuiltinCounterStyle(type, this);
+        data = new (mPresContext) DependentBuiltinCounterStyle(type, this);
       } else {
         data = GetBuiltinStyle(type);
       }
     }
   }
   if (!data) {
     data = GetDecimalStyle();
   }
--- a/layout/style/CounterStyleManager.h
+++ b/layout/style/CounterStyleManager.h
@@ -171,16 +171,18 @@ public:
   }
 
   // This method will scan all existing counter styles generated by this
   // manager, and remove or mark data dirty accordingly. It returns true
   // if any counter style is changed, false elsewise. This method should
   // be called when any counter style may be affected.
   bool NotifyRuleChanged();
 
+  nsPresContext* PresContext() const { return mPresContext; }
+
   NS_INLINE_DECL_REFCOUNTING(CounterStyleManager)
 
 private:
   nsPresContext* mPresContext;
   nsRefPtrHashtable<nsStringHashKey, CounterStyle> mCacheTable;
 };
 
 } // namespace mozilla
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -584,17 +584,18 @@ nsSVGIntegrationUtils::PaintFramesWithEf
       } else {
         gfx->Mask(clipMaskSurface, clippedMaskTransform);
       }
     }
   }
 
   if (maskSurface) {
     gfx->Mask(maskSurface, maskTransform);
-  } else if (opacity != 1.0f) {
+  } else if (opacity != 1.0f ||
+             aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     gfx->Paint(opacity);
   }
 
   gfx->Restore();
 }
 
 gfxMatrix
 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -665,17 +665,18 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
       } else {
         gfx->Mask(clipMaskSurface, clippedMaskTransform);
       }
     }
   }
 
   if (maskSurface) {
     gfx->Mask(maskSurface, maskTransform);
-  } else if (opacity != 1.0f) {
+  } else if (opacity != 1.0f ||
+             aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     gfx->Paint(opacity);
   }
 
   gfx->Restore();
 }
 
 bool
 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
--- a/mobile/android/base/tests/StringHelper.java
+++ b/mobile/android/base/tests/StringHelper.java
@@ -93,16 +93,17 @@ public class StringHelper {
     public static final String ROBOCOP_BLANK_PAGE_01_URL = "/robocop/robocop_blank_01.html";
     public static final String ROBOCOP_BLANK_PAGE_02_URL = "/robocop/robocop_blank_02.html";
     public static final String ROBOCOP_BLANK_PAGE_03_URL = "/robocop/robocop_blank_03.html";
     public static final String ROBOCOP_BLANK_PAGE_04_URL = "/robocop/robocop_blank_04.html";
     public static final String ROBOCOP_BLANK_PAGE_05_URL = "/robocop/robocop_blank_05.html";
     public static final String ROBOCOP_BOXES_URL = "/robocop/robocop_boxes.html";
     public static final String ROBOCOP_GEOLOCATION_URL = "/robocop/robocop_geolocation.html";
     public static final String ROBOCOP_LOGIN_URL = "/robocop/robocop_login.html";
+    public static final String ROBOCOP_POPUP_URL = "/robocop/robocop_popup.html";
     public static final String ROBOCOP_OFFLINE_STORAGE_URL = "/robocop/robocop_offline_storage.html";
     public static final String ROBOCOP_PICTURE_LINK_URL = "/robocop/robocop_picture_link.html";
     public static final String ROBOCOP_SEARCH_URL = "/robocop/robocop_search.html";
     public static final String ROBOCOP_TEXT_PAGE_URL = "/robocop/robocop_text_page.html";
     public static final String ROBOCOP_ADOBE_FLASH_URL = "/robocop/robocop_adobe_flash.html";
     public static final String ROBOCOP_INPUT_URL = "/robocop/robocop_input.html";
 
     private static final String ROBOCOP_JS_HARNESS_URL = "/robocop/robocop_javascript.html";
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/robocop_popup.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Page creating a popup</title>
+  </head>
+  <body>
+    <script type="text/javascript">
+      window.open("data:text/plain;charset=utf-8,a", "a");
+    </script>
+  </body>
+</html>
--- a/mobile/android/base/tests/testDoorHanger.java
+++ b/mobile/android/base/tests/testDoorHanger.java
@@ -152,16 +152,87 @@ public class testDoorHanger extends Base
         // Load login page
         inputAndLoadUrl(LOGIN_URL);
         waitForText(LOGIN_MESSAGE);
 
         // Test doorhanger is dismissed when tapping "Save" and is no longer triggered
         mSolo.clickOnButton(LOGIN_ALLOW);
         waitForTextDismissed(LOGIN_MESSAGE);
         mAsserter.is(mSolo.searchText(LOGIN_MESSAGE), false, "Login doorhanger notification is hidden when allowing saving password");
+
+        testPopupBlocking();
+    }
+
+    private void testPopupBlocking() {
+        String POPUP_URL = getAbsoluteUrl(StringHelper.ROBOCOP_POPUP_URL);
+        String POPUP_MESSAGE = "prevented this site from opening";
+        String POPUP_ALLOW = "Show";
+        String POPUP_DENY = "Don't show";
+
+        try {
+            JSONObject jsonPref = new JSONObject();
+            jsonPref.put("name", "dom.disable_open_during_load");
+            jsonPref.put("type", "bool");
+            jsonPref.put("value", true);
+            setPreferenceAndWaitForChange(jsonPref);
+        } catch (JSONException e) {
+            mAsserter.ok(false, "exception setting preference", e.toString());
+        }
+
+        // Load page with popup
+        inputAndLoadUrl(POPUP_URL);
+        waitForText(POPUP_MESSAGE);
+        mAsserter.is(mSolo.searchText(POPUP_MESSAGE), true, "Popup blocker is displayed");
+
+        // Wait for the popup to be shown.
+        Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
+
+        waitForCheckBox();
+        mSolo.clickOnCheckBox(0);
+        mSolo.clickOnButton(POPUP_ALLOW);
+        waitForTextDismissed(POPUP_MESSAGE);
+        mAsserter.is(mSolo.searchText(POPUP_MESSAGE), false, "Popup blocker is hidden when popup allowed");
+
+        try {
+            final JSONObject data = new JSONObject(tabEventExpecter.blockForEventData());
+
+            // Check to make sure the popup window was opened.
+            mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL");
+
+            // Close the popup window.
+            closeTab(data.getInt("tabID"));
+
+        } catch (JSONException e) {
+            mAsserter.ok(false, "exception getting event data", e.toString());
+        }
+        tabEventExpecter.unregisterListener();
+
+        // Load page with popup
+        inputAndLoadUrl(POPUP_URL);
+        waitForText(POPUP_MESSAGE);
+        mAsserter.is(mSolo.searchText(POPUP_MESSAGE), true, "Popup blocker is displayed");
+
+        waitForCheckBox();
+        mSolo.clickOnCheckBox(0);
+        mSolo.clickOnButton(POPUP_DENY);
+        waitForTextDismissed(POPUP_MESSAGE);
+        mAsserter.is(mSolo.searchText(POPUP_MESSAGE), false, "Popup blocker is hidden when popup denied");
+
+        // Check that we're on the same page to verify that the popup was not shown.
+        verifyUrl(POPUP_URL);
+
+        try {
+            JSONObject jsonPref = new JSONObject();
+            jsonPref.put("name", "dom.disable_open_during_load");
+            jsonPref.put("type", "bool");
+            jsonPref.put("value", false);
+            setPreferenceAndWaitForChange(jsonPref);
+        } catch (JSONException e) {
+            mAsserter.ok(false, "exception setting preference", e.toString());
+        }
     }
 
     // wait for a CheckBox view that is clickable
     private void waitForCheckBox() {
         waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
                 for (CheckBox view : mSolo.getCurrentViews(CheckBox.class)) {
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -354,16 +354,19 @@
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.manifest
+@BINPATH@/components/ProcessSingleton.manifest
+@BINPATH@/components/MainProcessSingleton.js
+@BINPATH@/components/ContentProcessSingleton.js
 @BINPATH@/components/nsSessionStore.manifest
 @BINPATH@/components/nsSessionStartup.js
 @BINPATH@/components/nsSessionStore.js
 @BINPATH@/components/nsURLFormatter.manifest
 @BINPATH@/components/nsURLFormatter.js
 @BINPATH@/components/@DLL_PREFIX@browsercomps@DLL_SUFFIX@
 @BINPATH@/components/txEXSLTRegExFunctions.manifest
 @BINPATH@/components/txEXSLTRegExFunctions.js
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1488,16 +1488,19 @@ pref("network.dns.ipv4OnlyDomains", "");
 pref("network.dns.disableIPv6", false);
 
 // This is the number of dns cache entries allowed
 pref("network.dnsCacheEntries", 400);
 
 // In the absence of OS TTLs, the DNS cache TTL value
 pref("network.dnsCacheExpiration", 60);
 
+// Get TTL; not supported on all platforms; nop on the unsupported ones.
+pref("network.dns.get-ttl", true);
+
 // The grace period allows the DNS cache to use expired entries, while kicking off
 // a revalidation in the background.
 pref("network.dnsCacheExpirationGracePeriod", 60);
 
 // This preference can be used to turn off DNS prefetch.
 pref("network.dns.disablePrefetch", false);
 
 // This preference controls whether or not URLs with UTF-8 characters are
@@ -4346,19 +4349,12 @@ pref("dom.fetch.enabled", false);
 // supported will be disabled. This threshold can be adjusted to suit other
 // platforms; and set to 0 to disable the low-memory check altogether.
 pref("camera.control.low_memory_thresholdMB", 404);
 #endif
 
 // UDPSocket API
 pref("dom.udpsocket.enabled", false);
 
-// Experiment: Get TTL from DNS records.
-//     Unset initially (0); Randomly chosen on first run; will remain unchanged
-//     unless adjusted by the user or experiment ends. Variants defined in
-//     nsHostResolver.cpp.
-pref("dns.ttl-experiment.variant", 0);
-pref("dns.ttl-experiment.enabled", true);
-
 // Use raw ICU instead of CoreServices API in Unicode collation
 #ifdef XP_MACOSX
 pref("intl.collation.mac.use_icu", true);
 #endif
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -149,169 +149,16 @@ IsMediumPriority(uint16_t flags)
 
 static inline bool
 IsLowPriority(uint16_t flags)
 {
     return flags & nsHostResolver::RES_PRIORITY_LOW;
 }
 
 //----------------------------------------------------------------------------
-
-enum DnsExpirationVariant {
-    DNS_EXP_VARIANT_UNSET = 0,
-    DNS_EXP_VARIANT_CONTROL = 1,
-    DNS_EXP_VARIANT_TTL_ONLY = 2,
-    DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE = 3,
-    DNS_EXP_VARIANT_MAX_VALUE = 3,
-};
-static DnsExpirationVariant sDnsVariant;
-
-static mozilla::Telemetry::ID
-GetCacheHitHistogram(DnsExpirationVariant aVariant,
-                     nsHostRecord::DnsPriority aPriority)
-{
-    using namespace mozilla::Telemetry;
-
-    switch (aVariant) {
-        case DNS_EXP_VARIANT_CONTROL:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_CACHE_HIT_VAR_CONTROL_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_CACHE_HIT_VAR_CONTROL_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_CACHE_HIT_VAR_CONTROL_LOW;
-            }
-
-        case DNS_EXP_VARIANT_TTL_ONLY:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_CACHE_HIT_VAR_TTL_ONLY_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_CACHE_HIT_VAR_TTL_ONLY_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_CACHE_HIT_VAR_TTL_ONLY_LOW;
-            }
-
-        case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_CACHE_HIT_VAR_TTL_PLUS_CONST_GRACE_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_CACHE_HIT_VAR_TTL_PLUS_CONST_GRACE_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_CACHE_HIT_VAR_TTL_PLUS_CONST_GRACE_LOW;
-            }
-
-        case DNS_EXP_VARIANT_UNSET:
-            ;
-    }
-
-    MOZ_ASSERT_UNREACHABLE("Invalid expiration variant.");
-    return DNS_CACHE_HIT_VAR_CONTROL_LOW;
-}
-
-static mozilla::Telemetry::ID
-GetBlacklistCountHistogram(DnsExpirationVariant aVariant,
-                           nsHostRecord::DnsPriority aPriority)
-{
-    using namespace mozilla::Telemetry;
-
-    switch (aVariant) {
-        case DNS_EXP_VARIANT_CONTROL:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_BLACKLIST_COUNT_VAR_CONTROL_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_BLACKLIST_COUNT_VAR_CONTROL_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_BLACKLIST_COUNT_VAR_CONTROL_LOW;
-            }
-
-        case DNS_EXP_VARIANT_TTL_ONLY:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_ONLY_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_ONLY_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_ONLY_LOW;
-            }
-
-        case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE:
-            switch (aPriority) {
-                case nsHostRecord::DNS_PRIORITY_HIGH:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_PLUS_CONST_GRACE_HIGH;
-
-                case nsHostRecord::DNS_PRIORITY_MEDIUM:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_PLUS_CONST_GRACE_MEDIUM;
-
-                case nsHostRecord::DNS_PRIORITY_LOW:
-                    return DNS_BLACKLIST_COUNT_VAR_TTL_PLUS_CONST_GRACE_LOW;
-            }
-
-        case DNS_EXP_VARIANT_UNSET:
-            ; // Fall through
-    }
-
-    MOZ_ASSERT_UNREACHABLE("Invalid variant.");
-    return DNS_BLACKLIST_COUNT_VAR_CONTROL_LOW;
-}
-
-static mozilla::Telemetry::ID
-GetRenewalTimeHistogram(DnsExpirationVariant aVariant)
-{
-    using namespace mozilla::Telemetry;
-
-#ifdef TTL_AVAILABLE
-    switch (sDnsVariant) {
-        case DNS_EXP_VARIANT_CONTROL:
-            return DNS_RENEWAL_TIME;
-        case DNS_EXP_VARIANT_TTL_ONLY:
-            return DNS_RENEWAL_TIME__TTL_ONLY_EXPT;
-        case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE:
-            return DNS_RENEWAL_TIME__TTL_PLUS_CONST_GRACE_EXPT;
-        default:
-            MOZ_ASSERT_UNREACHABLE("Invalid variant.");
-    }
-#endif
-    return DNS_RENEWAL_TIME;
-}
-
-static mozilla::Telemetry::ID
-GetRenewalTimeForTTLHistogram(DnsExpirationVariant aVariant)
-{
-    using namespace mozilla::Telemetry;
-
-#ifdef TTL_AVAILABLE
-    switch (sDnsVariant) {
-        case DNS_EXP_VARIANT_CONTROL:
-            MOZ_ASSERT_UNREACHABLE("No TTL for Control Expt.");
-            return DNS_RENEWAL_TIME;
-        case DNS_EXP_VARIANT_TTL_ONLY:
-            return DNS_RENEWAL_TIME_FOR_TTL__TTL_ONLY_EXPT;
-        case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE:
-            return DNS_RENEWAL_TIME_FOR_TTL__TTL_PLUS_CONST_GRACE_EXPT;
-        default:
-            MOZ_ASSERT_UNREACHABLE("Invalid variant.");
-    }
-#endif
-    return DNS_RENEWAL_TIME;
-}
-
 // this macro filters out any flags that are not used when constructing the
 // host key.  the significant flags are those that would affect the resulting
 // host record (i.e., the flags that are passed down to PR_GetAddrInfoByName).
 #define RES_KEY_FLAGS(_f) ((_f) & nsHostResolver::RES_CANON_NAME)
 
 nsHostRecord::nsHostRecord(const nsHostKey *key)
     : addr_info_lock("nsHostRecord.addr_info_lock")
     , addr_info_gencnt(0)
@@ -359,20 +206,17 @@ nsHostRecord::SetExpiration(const mozill
 {
     mValidStart = now;
     mGraceStart = now + TimeDuration::FromSeconds(valid);
     mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
 }
 
 nsHostRecord::~nsHostRecord()
 {
-    Telemetry::Accumulate(
-        GetBlacklistCountHistogram(sDnsVariant,
-                                   nsHostRecord::GetPriority(flags)),
-        mBlacklistedCount);
+    Telemetry::Accumulate(Telemetry::DNS_BLACKLIST_COUNT, mBlacklistedCount);
     delete addr_info;
     delete addr;
 }
 
 bool
 nsHostRecord::Blacklisted(NetAddr *aQuery)
 {
     // must call locked
@@ -651,83 +495,33 @@ HostDB_PruneEntry(PLDHashTable *table,
         return PL_DHASH_REMOVE;
     }
     return PL_DHASH_NEXT;
 }
 
 //----------------------------------------------------------------------------
 
 #if TTL_AVAILABLE
-static const char kTtlExperiment[] = "dns.ttl-experiment.";
-static const char kTtlExperimentEnabled[] = "dns.ttl-experiment.enabled";
-static const char kNetworkExperimentsEnabled[] = "network.allow-experiments";
-static const char kTtlExperimentVariant[] = "dns.ttl-experiment.variant";
-
-void
-nsHostResolver::DnsExperimentChangedInternal()
-{
-    MOZ_ASSERT(NS_IsMainThread(), "Can only get prefs on main thread!");
-
-    if (!Preferences::GetBool(kTtlExperimentEnabled) ||
-            !Preferences::GetBool(kNetworkExperimentsEnabled)) {
-        sDnsVariant = DNS_EXP_VARIANT_CONTROL;
-        LOG(("DNS TTL experiment is disabled."));
-        return;
-    }
-
-    auto variant = static_cast<DnsExpirationVariant>(
-        Preferences::GetInt(kTtlExperimentVariant));
+static const char kPrefGetTtl[] = "network.dns.get-ttl";
+static bool sGetTtlEnabled = false;
 
-    // Setup this profile to use a particular DNS expiration strategy
-    if (variant == DNS_EXP_VARIANT_UNSET) {
-        variant = static_cast<DnsExpirationVariant>(
-            rand() % DNS_EXP_VARIANT_MAX_VALUE + 1);
-        LOG(("No DNS TTL experiment variant saved. Randomly picked %d.",
-             variant));
-
-        DebugOnly<nsresult> rv = Preferences::SetInt(
-            kTtlExperimentVariant, variant);
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not set experiment variant pref.");
-    } else {
-        LOG(("Using saved DNS TTL experiment %d.", variant));
-    }
-
-    sDnsVariant = variant;
-}
-
-void
-nsHostResolver::DnsExperimentChanged(const char* aPref, void* aClosure)
+static void DnsPrefChanged(const char* aPref, void* aClosure)
 {
     MOZ_ASSERT(NS_IsMainThread(),
                "Should be getting pref changed notification on main thread!");
 
-    if (strcmp(aPref, kNetworkExperimentsEnabled) != 0 &&
-        strncmp(aPref, kTtlExperiment, strlen(kTtlExperiment)) != 0) {
-        LOG(("DnsExperimentChanged ignoring pref \"%s\"", aPref));
+    if (strcmp(aPref, kPrefGetTtl) != 0) {
+        LOG(("DnsPrefChanged ignoring pref \"%s\"", aPref));
         return;
     }
 
     auto self = static_cast<nsHostResolver*>(aClosure);
     MOZ_ASSERT(self);
 
-    // We can't set a pref in the context of a pref change callback, so
-    // dispatch DnsExperimentChangedInternal for async getting/setting.
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(
-        NS_NewRunnableMethod(self, &nsHostResolver::DnsExperimentChangedInternal));
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                     "Could not dispatch DnsExperimentChanged event.");
-}
-
-void
-nsHostResolver::InitCRandom()
-{
-    MOZ_ASSERT(NS_IsMainThread(), "Should be seeding rand() on main thread!");
-
-    srand(time(nullptr));
+    sGetTtlEnabled = Preferences::GetBool(kPrefGetTtl);
 }
 #endif
 
 nsHostResolver::nsHostResolver(uint32_t maxCacheEntries,
                                uint32_t defaultCacheEntryLifetime,
                                uint32_t defaultGracePeriod)
     : mMaxCacheEntries(maxCacheEntries)
     , mDefaultCacheLifetime(defaultCacheEntryLifetime)
@@ -762,36 +556,26 @@ nsHostResolver::Init()
     if (NS_FAILED(GetAddrInfoInit())) {
         return NS_ERROR_FAILURE;
     }
 
     PL_DHashTableInit(&mDB, &gHostDB_ops, nullptr, sizeof(nsHostDBEnt), 0);
 
     mShutdown = false;
 
-    sDnsVariant = DNS_EXP_VARIANT_CONTROL;
-
 #if TTL_AVAILABLE
     // The preferences probably haven't been loaded from the disk yet, so we
     // need to register a callback that will set up the experiment once they
     // are. We also need to explicitly set a value for the props otherwise the
     // callback won't be called.
     {
-        DebugOnly<nsresult> rv = NS_DispatchToMainThread(
-            NS_NewRunnableMethod(this, &nsHostResolver::InitCRandom));
+        DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
+            &DnsPrefChanged, kPrefGetTtl, this);
         NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not dispatch InitCRandom event.");
-        rv = Preferences::RegisterCallbackAndCall(
-            &DnsExperimentChanged, kTtlExperiment, this);
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not register DNS experiment callback.");
-        rv = Preferences::RegisterCallback(
-            &DnsExperimentChanged, kNetworkExperimentsEnabled, this);
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not register network experiment callback.");
+                         "Could not register DNS TTL pref callback.");
     }
 #endif
 
 #if defined(HAVE_RES_NINIT)
     // We want to make sure the system is using the correct resolver settings,
     // so we force it to reload those settings whenever we startup a subsequent
     // nsHostResolver instance.  We assume that there is no reason to do this
     // for the first nsHostResolver instance since that is usually created
@@ -855,23 +639,19 @@ nsHostResolver::FlushCache()
 void
 nsHostResolver::Shutdown()
 {
     LOG(("Shutting down host resolver.\n"));
 
 #if TTL_AVAILABLE
     {
         DebugOnly<nsresult> rv = Preferences::UnregisterCallback(
-            &DnsExperimentChanged, kTtlExperiment, this);
+            &DnsPrefChanged, kPrefGetTtl, this);
         NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not unregister TTL experiment callback.");
-        rv = Preferences::UnregisterCallback(
-            &DnsExperimentChanged, kNetworkExperimentsEnabled, this);
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
-                         "Could not unregister network experiment callback.");
+                         "Could not unregister DNS TTL pref callback.");
     }
 #endif
 
     PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ;
     PR_INIT_CLIST(&pendingQHigh);
     PR_INIT_CLIST(&pendingQMed);
     PR_INIT_CLIST(&pendingQLow);
     PR_INIT_CLIST(&evictionQ);
@@ -1159,19 +939,16 @@ nsHostResolver::ResolveHost(const char  
                 }
             }
         }
     }
     if (result) {
         callback->OnLookupComplete(this, result, status);
     }
 
-    Telemetry::Accumulate(
-        GetCacheHitHistogram(sDnsVariant, nsHostRecord::GetPriority(flags)),
-        static_cast<bool>(result));
     return rv;
 }
 
 void
 nsHostResolver::DetachCallback(const char            *host,
                                uint16_t               flags,
                                uint16_t               af,
                                nsResolveHostCallback *callback,
@@ -1324,17 +1101,17 @@ nsHostResolver::GetHostToLookup(nsHostRe
     timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout;
     epoch = PR_IntervalNow();
 
     while (!mShutdown) {
         // remove next record from Q; hand over owning reference. Check high, then med, then low
         
 #if TTL_AVAILABLE
         #define SET_GET_TTL(var, val) \
-            (var)->mGetTtl = sDnsVariant != DNS_EXP_VARIANT_CONTROL && (val)
+            (var)->mGetTtl = sGetTtlEnabled && (val)
 #else
         #define SET_GET_TTL(var, val)
 #endif
 
         if (!PR_CLIST_IS_EMPTY(&mHighQ)) {
             DeQueue (mHighQ, result);
             SET_GET_TTL(*result, false);
             return true;
@@ -1397,49 +1174,33 @@ nsHostResolver::PrepareRecordExpiration(
     if (!rec->addr_info) {
         rec->SetExpiration(TimeStamp::NowLoRes(),
                            NEGATIVE_RECORD_LIFETIME, 0);
         LOG(("Caching [%s] negative record for %u seconds.\n", rec->host,
              NEGATIVE_RECORD_LIFETIME));
         return;
     }
 
-    unsigned int ttl = mDefaultCacheLifetime;
+    unsigned int lifetime = mDefaultCacheLifetime;
+    unsigned int grace = mDefaultGracePeriod;
 #if TTL_AVAILABLE
-    if (sDnsVariant == DNS_EXP_VARIANT_TTL_ONLY
-            || sDnsVariant == DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE) {
+    unsigned int ttl = mDefaultCacheLifetime;
+    if (sGetTtlEnabled) {
         MutexAutoLock lock(rec->addr_info_lock);
         if (rec->addr_info && rec->addr_info->ttl != AddrInfo::NO_TTL_DATA) {
             ttl = rec->addr_info->ttl;
         }
+        lifetime = ttl;
+        grace = 0;
     }
 #endif
 
-    unsigned int lifetime = 0;
-    unsigned int grace = 0;
-    switch (sDnsVariant) {
-        case DNS_EXP_VARIANT_TTL_ONLY:
-            lifetime = ttl;
-            grace = 0;
-            break;
-
-        case DNS_EXP_VARIANT_TTL_PLUS_CONST_GRACE:
-            lifetime = ttl;
-            grace = mDefaultGracePeriod;
-            break;
-
-        default:
-            lifetime = mDefaultCacheLifetime;
-            grace = mDefaultGracePeriod;
-            break;
-    }
-
     rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
-    LOG(("Caching [%s] record for %u seconds (grace %d) (sDnsVariant = %d).",
-         rec->host, lifetime, grace, sDnsVariant));
+    LOG(("Caching [%s] record for %u seconds (grace %d).",
+         rec->host, lifetime, grace));
 }
 
 //
 // OnLookupComplete() checks if the resolving should be redone and if so it
 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
 //
 
 nsHostResolver::LookupStatus
@@ -1499,18 +1260,17 @@ nsHostResolver::OnLookupComplete(nsHostR
                     Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
                                           static_cast<uint32_t>(age.ToSeconds() / 60));
                 }
 
                 // release reference to rec owned by mEvictionQ
                 NS_RELEASE(head);
             }
 #if TTL_AVAILABLE
-            if (!rec->mGetTtl && sDnsVariant != DNS_EXP_VARIANT_CONTROL
-                && !rec->resolving) {
+            if (!rec->mGetTtl && !rec->resolving && sGetTtlEnabled) {
                 LOG(("Issuing second async lookup for TTL for %s.", rec->host));
                 rec->flags =
                   (rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
                 DebugOnly<nsresult> rv = IssueLookup(rec);
                 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                                  "Could not issue second async lookup for TTL.");
             }
 #endif
@@ -1652,20 +1412,20 @@ nsHostResolver::ThreadFunc(void *arg)
 
         if (NS_SUCCEEDED(status)) {
             Telemetry::ID histogramID;
             if (!rec->addr_info_gencnt) {
                 // Time for initial lookup.
                 histogramID = Telemetry::DNS_LOOKUP_TIME;
             } else if (!getTtl) {
                 // Time for renewal; categorized by expiration strategy.
-                histogramID = GetRenewalTimeHistogram(sDnsVariant);
+                histogramID = Telemetry::DNS_RENEWAL_TIME;
             } else {
                 // Time to get TTL; categorized by expiration strategy.
-                histogramID = GetRenewalTimeForTTLHistogram(sDnsVariant);
+                histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
             }
             Telemetry::Accumulate(histogramID, millis);
         } else {
             Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
         }
 
         // OnLookupComplete may release "rec", long before we lose it.
         LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -310,32 +310,16 @@ private:
     void     ClearPendingQueue(PRCList *aPendingQueue);
     nsresult ConditionallyCreateThread(nsHostRecord *rec);
 
     /**
      * Starts a new lookup in the background for entries that are in the grace
      * period with a failed connect or all cached entries are negative.
      */
     nsresult ConditionallyRefreshRecord(nsHostRecord *rec, const char *host);
-    
-#if TTL_AVAILABLE
-    // For DNS TTL Experiments.
-
-    // Internal function which initializes the TTL experiment pref to a random
-    // value corresponding to one of the TTL experiment variants. To be
-    // dispatched by DnsExperimentChanged to the main thread, since setting
-    // prefs can't be done in the context of a "pref changed" callback.
-    void DnsExperimentChangedInternal();
-
-    // Callback to be registered with Preferences::RegisterCallback.
-    static void DnsExperimentChanged(const char* aPref, void* aClosure);
-
-    // Dispatched to the main thread to ensure that rand is seeded.
-    void InitCRandom();
-#endif
 
     static void  MoveQueue(nsHostRecord *aRec, PRCList &aDestQ);
     
     static void ThreadFunc(void *);
 
     enum {
         METHOD_HIT = 1,
         METHOD_RENEWAL = 2,
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -998,16 +998,29 @@ Http2Session::CleanupStream(Http2Stream 
     Close(NS_OK);
 
   if (pushSource) {
     pushSource->SetDeferCleanupOnSuccess(false);
     CleanupStream(pushSource, aResult, aResetCode);
   }
 }
 
+void
+Http2Session::CleanupStream(uint32_t aID, nsresult aResult, errorType aResetCode)
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  Http2Stream *stream = mStreamIDHash.Get(aID);
+  LOG3(("Http2Session::CleanupStream %p by ID 0x%X to stream %p\n",
+        this, aID, stream));
+  if (!stream) {
+    return;
+  }
+  CleanupStream(stream, aResult, aResetCode);
+}
+
 static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue)
 {
   uint32_t size = queue.GetSize();
   for (uint32_t count = 0; count < size; ++count) {
     Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront());
     if (stream != aStream)
       queue.Push(stream);
   }
@@ -2555,17 +2568,17 @@ Http2Session::WriteSegments(nsAHttpSegme
   if (mDownstreamState == PROCESSING_DATA_FRAME ||
       mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
 
     // The cleanup stream should only be set while stream->WriteSegments is
     // on the stack and then cleaned up in this code block afterwards.
     MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
     mNeedsCleanup = nullptr;                     /* just in case */
 
-    Http2Stream *stream = mInputFrameDataStream;
+    uint32_t streamID = mInputFrameDataStream->StreamID();
     mSegmentWriter = writer;
     rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
     mSegmentWriter = nullptr;
 
     mLastDataReadEpoch = mLastReadEpoch;
 
     if (SoftStreamError(rv)) {
       // This will happen when the transaction figures out it is EOF, generally
@@ -2573,23 +2586,22 @@ Http2Session::WriteSegments(nsAHttpSegme
       // otherwise the whole session would be torn down.
 
       // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
       // back to PROCESSING_DATA_FRAME where we came from
       mDownstreamState = PROCESSING_DATA_FRAME;
 
       if (mInputFrameDataRead == mInputFrameDataSize)
         ResetDownstreamState();
-      LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
+      LOG3(("Http2Session::WriteSegments session=%p id 0x%X "
             "needscleanup=%p. cleanup stream based on "
             "stream->writeSegments returning code %x\n",
-            this, stream, stream ? stream->StreamID() : 0,
-            mNeedsCleanup, rv));
-      CleanupStream(stream, NS_OK, CANCEL_ERROR);
-      MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup == stream);
+            this, streamID, mNeedsCleanup, rv));
+      CleanupStream(streamID, NS_OK, CANCEL_ERROR);
+      MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup->StreamID() == streamID);
       mNeedsCleanup = nullptr;
       return NS_OK;
     }
 
     if (mNeedsCleanup) {
       LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
             "cleanup stream based on mNeedsCleanup.\n",
             this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -241,16 +241,17 @@ private:
   nsresult    ReadyToProcessDataFrame(enum internalStateType);
   nsresult    UncompressAndDiscard();
   void        GeneratePing(bool);
   void        GenerateSettingsAck();
   void        GeneratePriority(uint32_t, uint8_t);
   void        GenerateRstStream(uint32_t, uint32_t);
   void        GenerateGoAway(uint32_t);
   void        CleanupStream(Http2Stream *, nsresult, errorType);
+  void        CleanupStream(uint32_t, nsresult, errorType);
   void        CloseStream(Http2Stream *, nsresult);
   void        SendHello();
   void        RemoveStreamFromQueues(Http2Stream *);
   nsresult    ParsePadding(uint8_t &, uint16_t &);
 
   void        SetWriteCallbacks();
   void        RealignOutputQueue();
 
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -13,17 +13,17 @@ try:
     from urllib2 import urlopen
 except ImportError:
     from urllib.request import urlopen
 
 from distutils.version import StrictVersion
 
 from mozboot.base import BaseBootstrapper
 
-HOMEBREW_BOOTSTRAP = 'https://raw.github.com/Homebrew/homebrew/go/install'
+HOMEBREW_BOOTSTRAP = 'https://raw.githubusercontent.com/Homebrew/install/master/install'
 XCODE_APP_STORE = 'macappstore://itunes.apple.com/app/id497799835?mt=12'
 XCODE_LEGACY = 'https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_3.2.6_and_ios_sdk_4.3__final/xcode_3.2.6_and_ios_sdk_4.3.dmg'
 HOMEBREW_AUTOCONF213 = 'https://raw.github.com/Homebrew/homebrew-versions/master/autoconf213.rb'
 
 MACPORTS_URL = {'9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.2.1-10.9-Mavericks.pkg',
                 '8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.8-MountainLion.pkg',
                 '7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.7-Lion.pkg',
                 '6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.6-SnowLeopard.pkg',}
--- a/services/crypto/modules/WeaveCrypto.js
+++ b/services/crypto/modules/WeaveCrypto.js
@@ -127,27 +127,26 @@ WeaveCrypto.prototype = {
         // implicitly initialize NSS.
         Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
 
         // Open the NSS library.
         let path = ctypes.libraryName("nss3");
 
         // XXX really want to be able to pass specific dlopen flags here.
         var nsslib;
-        try {
-            this.log("Trying NSS library without path");
-            nsslib = ctypes.open(path);
-        } catch(e) {
-            // In case opening the library without a full path fails,
-            // try again with a full path.
-            let file = Services.dirsvc.get("GreBinD", Ci.nsILocalFile);
-            file.append(path);
-            this.log("Trying again with path " + file.path);
-            nsslib = ctypes.open(file.path);
-        }
+#ifdef MOZ_NATIVE_NSS
+        // Search platform-dependent library paths for system NSS.
+        this.log("Trying NSS library without path");
+        nsslib = ctypes.open(path);
+#else
+        let file = Services.dirsvc.get("GreBinD", Ci.nsILocalFile);
+        file.append(path);
+        this.log("Trying NSS library with path " + file.path);
+        nsslib = ctypes.open(file.path);
+#endif
 
         this.log("Initializing NSS types and function declarations...");
 
         this.nss = {};
         this.nss_t = {};
 
         // nsprpub/pr/include/prtypes.h#435
         // typedef PRIntn PRBool; --> int
--- a/services/crypto/moz.build
+++ b/services/crypto/moz.build
@@ -4,14 +4,20 @@
 # 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/.
 
 DIRS += ['component']
 TEST_DIRS += ['tests']
 
 EXTRA_JS_MODULES['services-crypto'] += [
     'modules/utils.js',
+]
+
+EXTRA_PP_JS_MODULES['services-crypto'] += [
     'modules/WeaveCrypto.js',
 ]
 
 EXTRA_COMPONENTS += [
     'cryptoComponents.manifest',
 ]
+
+if CONFIG['MOZ_NATIVE_NSS']:
+    DEFINES['MOZ_NATIVE_NSS'] = 1
--- a/testing/marionette/client/docs/index.rst
+++ b/testing/marionette/client/docs/index.rst
@@ -147,55 +147,33 @@ Debugging
 .. autoattribute:: Marionette.page_source
 .. automethod:: Marionette.log
 .. automethod:: Marionette.get_logs
 .. automethod:: Marionette.screenshot
 
 Querying and Modifying Document Content
 ---------------------------------------