Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 25 Feb 2016 14:42:14 -0800
changeset 285603 8e34b12969bf345b0cd765f612d59f8c0a6de444
parent 285563 c75bc39acae8f6dc6f66cf3e479fc2ddd301c08a (current diff)
parent 285602 918df3a0bc1c4d07299e4f66274a7da923534577 (diff)
child 285604 8a390011304e8adb8abe4880792148f6a3a9bf56
push id17831
push userkwierso@gmail.com
push dateThu, 25 Feb 2016 22:42:17 +0000
treeherderfx-team@8e34b12969bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
Merge m-c to fx-team, a=merge MozReview-Commit-ID: BUCPntgguRg
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1425,31 +1425,20 @@ void
 DocAccessible::CacheChildren()
 {
   // Search for accessible children starting from the document element since
   // some web pages tend to insert elements under it rather than document body.
   dom::Element* rootElm = mDocumentNode->GetRootElement();
   if (!rootElm)
     return;
 
-  // Ignore last HTML:br, copied from HyperTextAccessible.
   TreeWalker walker(this, rootElm);
-  Accessible* lastChild = nullptr;
-  while (Accessible* child = walker.Next()) {
-    if (lastChild)
-      AppendChild(lastChild);
-
-    lastChild = child;
-  }
-
-  if (lastChild) {
-    if (lastChild->IsHTMLBr())
-      Document()->UnbindFromDocument(lastChild);
-    else
-      AppendChild(lastChild);
+  Accessible* child = nullptr;
+  while ((child = walker.Next())) {
+    AppendChild(child);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 DocAccessible::NotifyOfLoading(bool aIsReloading)
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1930,41 +1930,16 @@ HyperTextAccessible::RelationByType(Rela
       break;
     default:
       break;
   }
 
   return rel;
 }
 
-void
-HyperTextAccessible::CacheChildren()
-{
-  // Trailing HTML br element don't play any difference. We don't need to expose
-  // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
-  // for details).
-
-  TreeWalker walker(this, mContent);
-  Accessible* child = nullptr;
-  Accessible* lastChild = nullptr;
-  while ((child = walker.Next())) {
-    if (lastChild)
-      AppendChild(lastChild);
-
-    lastChild = child;
-  }
-
-  if (lastChild) {
-    if (lastChild->IsHTMLBr())
-      Document()->UnbindFromDocument(lastChild);
-    else
-      AppendChild(lastChild);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible public static
 
 nsresult
 HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
                                              uint32_t* aRenderedOffset) const
 {
   if (!aFrame) {
--- a/accessible/generic/HyperTextAccessible.h
+++ b/accessible/generic/HyperTextAccessible.h
@@ -429,17 +429,16 @@ public:
    */
   dom::Selection* DOMSelection() const;
 
 protected:
   virtual ~HyperTextAccessible() { }
 
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) override;
-  virtual void CacheChildren() override;
 
   // HyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   index_t ConvertMagicOffset(int32_t aOffset) const;
 
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -121,19 +121,33 @@ function dumpTree(aId, aMsg)
 
     var children = acc.children;
     for (var i = 0; i < children.length; i++) {
       var child = children.queryElementAt(i, nsIAccessible);
       dumpTreeIntl(child, indent + "  ");
     }
   }
 
+  function dumpDOMTreeIntl(node, indent)
+  {
+    dump(indent + prettyName(node) + "\n");
+
+    var children = node.childNodes;
+    for (var i = 0; i < children.length; i++) {
+      var child = children.item(i);
+      dumpDOMTreeIntl(child, indent + "  ");
+    }
+  }
+
   dump(aMsg + "\n");
   var root = getAccessible(aId);
   dumpTreeIntl(root, "  ");
+
+  dump("DOM tree:\n");
+  dumpDOMTreeIntl(getNode(aId), "  ");
 }
 
 /**
  * Invokes the given function when document is loaded and focused. Preferable
  * to mochitests 'addLoadEvent' function -- additionally ensures state of the
  * document accessible is not busy.
  *
  * @param aFunc  the function to invoke
--- a/accessible/tests/mochitest/editabletext/editabletext.js
+++ b/accessible/tests/mochitest/editabletext/editabletext.js
@@ -57,16 +57,17 @@ function editableTextTest(aID)
    * setTextContents test.
    */
   this.setTextContents = function setTextContents(aValue, aSkipStartOffset)
   {
     var testID = "setTextContents '" + aValue + "' for " + prettyName(aID);
 
     function setTextContentsInvoke()
     {
+      dump(`\nsetTextContents '${aValue}'\n`);
       var acc = getAccessible(aID, nsIAccessibleEditableText);
       acc.setTextContents(aValue);
     }
 
     aSkipStartOffset = aSkipStartOffset || 0;
     var insertTripple = aValue ?
       [ aSkipStartOffset, aSkipStartOffset + aValue.length, aValue ] : null;
     var oldValue = getValue(aID);
@@ -82,16 +83,17 @@ function editableTextTest(aID)
    */
   this.insertText = function insertText(aStr, aPos, aResStr, aResPos)
   {
     var testID = "insertText '" + aStr + "' at " + aPos + " for " +
       prettyName(aID);
 
     function insertTextInvoke()
     {
+      dump(`\ninsertText '${aStr}' at ${aPos} pos\n`);
       var acc = getAccessible(aID, nsIAccessibleEditableText);
       acc.insertText(aStr, aPos);
     }
 
     var resPos = (aResPos != undefined) ? aResPos : aPos;
     this.generateTest(aID, null, [resPos, resPos + aStr.length, aStr],
                       insertTextInvoke, getValueChecker(aID, aResStr), testID);
   }
--- a/dom/animation/CSSPseudoElement.cpp
+++ b/dom/animation/CSSPseudoElement.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/CSSPseudoElement.h"
 #include "mozilla/dom/CSSPseudoElementBinding.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/AnimationComparator.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CSSPseudoElement, mParentElement)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CSSPseudoElement, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CSSPseudoElement, Release)
@@ -46,18 +47,23 @@ JSObject*
 CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return CSSPseudoElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 CSSPseudoElement::GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal)
 {
-  // Bug 1234403: Implement this API.
-  NS_NOTREACHED("CSSPseudoElement::GetAnimations() is not implemented yet.");
+  nsIDocument* doc = mParentElement->GetComposedDoc();
+  if (doc) {
+    doc->FlushPendingNotifications(Flush_Style);
+  }
+
+  Element::GetAnimationsUnsorted(mParentElement, mPseudoType, aRetVal);
+  aRetVal.Sort(AnimationPtrComparator<RefPtr<Animation>>());
 }
 
 already_AddRefed<Animation>
 CSSPseudoElement::Animate(
     JSContext* aContext,
     JS::Handle<JSObject*> aFrames,
     const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
     ErrorResult& aError)
--- a/dom/animation/test/css-animations/file_document-get-animations.html
+++ b/dom/animation/test/css-animations/file_document-get-animations.html
@@ -9,20 +9,16 @@
   to { top: 100px }
 }
 @keyframes animBottom {
   to { bottom: 100px }
 }
 @keyframes animRight {
   to { right: 100px }
 }
-.with-before-animation::before {
-  content: " ";
-  animation: animLeft 100s;
-}
 </style>
 <body>
 <script>
 'use strict';
 
 test(function(t) {
   assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
@@ -240,20 +236,41 @@ test(function(t) {
   anim.cancel();
   anim.play();
   assert_equals(document.getAnimations().length, 1,
                 'CSS animations cancelled and restarted by the API are ' +
                 'returned');
 }, 'CSS Animations cancelled and restarted via the API are returned');
 
 test(function(t) {
-  var div = addDiv(t, { class: 'with-before-animation' });
-  // FIXME: This should actually return the animation on the pseudo element
-  // but we haven't implemented a PseudoElement interface to use as
-  // animation's target (bug 1174575) so we simply don't return these animations
-  // until we can support them properly.
-  assert_equals(document.getAnimations().length, 0,
-                'CSS animations on pseudo elements are not returned');
-}, 'CSS Animations targetting pseudo-elements are not returned');
+  addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
+                '#parent::before': 'animation: animRight 10s;' });
+  // create two divs with these arrangement:
+  //       parent
+  //     ::before,
+  //     ::after
+  //        |
+  //       child
+  var parent = addDiv(t, { 'id': 'parent' });
+  var child = addDiv(t);
+  parent.appendChild(child);
+  [parent, child].forEach((div) => {
+    div.setAttribute('style', 'animation: animBottom 10s');
+  });
+
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 4,
+                'CSS animations on both pseudo-elements and elements ' +
+                'are returned');
+  assert_equals(anims[0].effect.target, parent,
+                'The animation targeting the parent element comes first');
+  assert_equals(anims[1].effect.target.type, '::before',
+                'The animation targeting the ::before element comes second');
+  assert_equals(anims[2].effect.target.type, '::after',
+                'The animation targeting the ::after element comes third');
+  assert_equals(anims[3].effect.target, child,
+                'The animation targeting the child element comes last');
+}, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
+   'after sorting');
 
 done();
 </script>
 </body>
--- a/dom/animation/test/css-animations/file_effect-target.html
+++ b/dom/animation/test/css-animations/file_effect-target.html
@@ -9,13 +9,46 @@
 'use strict';
 
 test(function(t) {
   var div = addDiv(t);
   div.style.animation = 'anim 100s';
   var animation = div.getAnimations()[0];
   assert_equals(animation.effect.target, div,
     'Animation.target is the animatable div');
-}, 'Returned CSS animations have the correct Animation.target');
+}, 'Returned CSS animations have the correct effect target');
+
+test(function(t) {
+  addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
+  var div = addDiv(t, { class: 'after' });
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 2,
+                'Got animations running on ::after pseudo element');
+  assert_equals(anims[0].effect.target, anims[1].effect.target,
+                'Both animations return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+  addStyle(t, { '.after::after': 'animation: anim 10s;' });
+  var div = addDiv(t, { class: 'after' });
+  var pseudoTarget = document.getAnimations()[0].effect.target;
+  var effect = new KeyframeEffectReadOnly(pseudoTarget,
+                                          { background: ["blue", "red"] },
+                                          3000);
+  var newAnim = new Animation(effect, document.timeline);
+  newAnim.play();
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 2,
+                'Got animations running on ::after pseudo element');
+  assert_not_equals(anims[0], newAnim,
+                    'The scriped-generated animation appears last');
+  assert_equals(newAnim.effect.target, pseudoTarget,
+                'The effect.target of the scripted-generated animation is ' +
+                'the same as the one from the argument of ' +
+                'KeyframeEffectReadOnly constructor');
+  assert_equals(anims[0].effect.target, newAnim.effect.target,
+                'Both animations return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+   'CSSPseudoElement object as that from the CSS generated animation');
 
 done();
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_pseudoElement-get-animations.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+@keyframes anim1 { }
+@keyframes anim2 { }
+.before::before {
+  animation: anim1 10s;
+}
+.after-with-mix-anims-trans::after {
+  content: '';
+  animation: anim1 10s, anim2 10s;
+  width: 0px;
+  height: 0px;
+  transition: all 100s;
+}
+.after-change::after {
+  width: 100px;
+  height: 100px;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+  var div = addDiv(t, { class: 'before' });
+  var pseudoTarget = document.getAnimations()[0].effect.target;
+  assert_equals(pseudoTarget.getAnimations().length, 1,
+                'Expected number of animations are returned');
+  assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
+                'CSS animation name matches');
+}, 'getAnimations returns CSSAnimation objects');
+
+test(function(t) {
+  var div = addDiv(t, { class: 'after-with-mix-anims-trans' });
+  // Trigger transitions
+  flushComputedStyle(div);
+  div.classList.add('after-change');
+
+  // Create additional animation on the pseudo-element from script
+  var pseudoTarget = document.getAnimations()[0].effect.target;
+  var effect = new KeyframeEffectReadOnly(pseudoTarget,
+                                          { background: ["blue", "red"] },
+                                          3000);
+  var newAnimation = new Animation(effect, document.timeline);
+  newAnimation.id = 'scripted-anim';
+  newAnimation.play();
+
+  // Check order - the script-generated animation should appear later
+  var anims = pseudoTarget.getAnimations();
+  assert_equals(anims.length, 5,
+                'Got expected number of animations/trnasitions running on ' +
+                '::after pseudo element');
+  assert_equals(anims[0].transitionProperty, 'height',
+                '1st animation is the 1st transition sorted by name');
+  assert_equals(anims[1].transitionProperty, 'width',
+                '2nd animation is the 2nd transition sorted by name ');
+  assert_equals(anims[2].animationName, 'anim1',
+                '3rd animation is the 1st animation in animation-name list');
+  assert_equals(anims[3].animationName, 'anim2',
+                '4rd animation is the 2nd animation in animation-name list');
+  assert_equals(anims[4].id, 'scripted-anim',
+                'Animation added by script appears last');
+}, 'getAnimations returns css transitions/animations, and script-generated ' +
+   'animations in the expected order');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+  { "set": [["dom.animations-api.core.enabled", true]]},
+  function() {
+    window.open("file_pseudoElement-get-animations.html");
+  });
+</script>
--- a/dom/animation/test/css-transitions/file_document-get-animations.html
+++ b/dom/animation/test/css-transitions/file_document-get-animations.html
@@ -26,16 +26,59 @@ test(function(t) {
                 'getAnimations returns two running CSS Transitions');
 
   // Remove both
   div.style.transitionProperty = 'none';
   assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Transitions');
 }, 'getAnimations for CSS Transitions');
 
+test(function(t) {
+  addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
+                                'transition: all 100s;',
+                '.init::before': 'content: ""; width: 0px; ' +
+                                 'transition: all 10s;',
+                '.change::after': 'width: 100px;',
+                '.change::before': 'width: 100px;' });
+  // create two divs with these arrangement:
+  //       parent
+  //     ::before,
+  //     ::after
+  //        |
+  //       child
+  var parent = addDiv(t);
+  var child = addDiv(t);
+  parent.appendChild(child);
+
+  parent.style.left = '0px';
+  parent.style.transition = 'left 10s';
+  parent.classList.add('init');
+  child.style.left = '0px';
+  child.style.transition = 'left 10s';
+  flushComputedStyle(parent);
+
+  parent.style.left = '100px';
+  parent.classList.add('change');
+  child.style.left = '100px';
+
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 4,
+                'CSS transition on both pseudo-elements and elements ' +
+                'are returned');
+  assert_equals(anims[0].effect.target, parent,
+                'The animation targeting the parent element comes first');
+  assert_equals(anims[1].effect.target.type, '::before',
+                'The animation targeting the ::before element comes second');
+  assert_equals(anims[2].effect.target.type, '::after',
+                'The animation targeting the ::after element comes third');
+  assert_equals(anims[3].effect.target, child,
+                'The animation targeting the child element comes last');
+}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
+   'after sorting');
+
 async_test(function(t) {
   var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
   flushComputedStyle(div);
 
   div.style.left = '100px';
   var animations = div.getAnimations();
   assert_equals(animations.length, 1, 'Got transition');
   animations[0].finished.then(t.step_func(function() {
--- a/dom/animation/test/css-transitions/file_effect-target.html
+++ b/dom/animation/test/css-transitions/file_effect-target.html
@@ -13,11 +13,54 @@ test(function(t) {
   div.style.transition = 'left 100s';
   div.style.left = '100px';
 
   var animation = div.getAnimations()[0];
   assert_equals(animation.effect.target, div,
     'Animation.target is the animatable div');
 }, 'Returned CSS transitions have the correct Animation.target');
 
+test(function(t) {
+  addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
+                                'transition: all 10s;',
+                '.change::after': 'width: 100px; height: 100px;' });
+  var div = addDiv(t, { class: 'init' });
+  flushComputedStyle(div);
+  div.classList.add('change');
+
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 2,
+                'Got transitions running on ::after pseudo element');
+  assert_equals(anims[0].effect.target, anims[1].effect.target,
+                'Both transitions return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+  addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
+                '.change::after': 'width: 100px;' });
+  var div = addDiv(t, { class: 'init' });
+  flushComputedStyle(div);
+  div.classList.add('change');
+  var pseudoTarget = document.getAnimations()[0].effect.target;
+  var effect = new KeyframeEffectReadOnly(pseudoTarget,
+                                          { background: ["blue", "red"] },
+                                          3000);
+  var newAnim = new Animation(effect, document.timeline);
+  newAnim.play();
+
+  var anims = document.getAnimations();
+  assert_equals(anims.length, 2,
+                'Got animations running on ::after pseudo element');
+  assert_not_equals(anims[0], newAnim,
+                    'The scriped-generated animation appears last');
+  assert_equals(newAnim.effect.target, pseudoTarget,
+                'The effect.target of the scripted-generated animation is ' +
+                'the same as the one from the argument of ' +
+                'KeyframeEffectReadOnly constructor');
+  assert_equals(anims[0].effect.target, newAnim.effect.target,
+                'Both the transition and the scripted-generated animation ' +
+                'return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+   'CSSPseudoElement object as that from the CSS generated transition');
+
 done();
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_pseudoElement-get-animations.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+.init::before {
+  content: '';
+  height: 0px;
+  width: 0px;
+  opacity: 0;
+  transition: all 100s;
+}
+.change::before {
+  height: 100px;
+  width: 100px;
+  opacity: 1;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+  var div = addDiv(t, { class: 'init' });
+  flushComputedStyle(div);
+  div.classList.add('change');
+
+  // Sanity checks
+  assert_equals(document.getAnimations().length, 3,
+                'Got expected number of animations on document');
+  var pseudoTarget = document.getAnimations()[0].effect.target;
+  assert_class_string(pseudoTarget, 'CSSPseudoElement',
+                      'Got pseudo-element target');
+
+  // Check animations returned from the pseudo element are in correct order
+  var anims = pseudoTarget.getAnimations();
+  assert_equals(anims.length, 3,
+                'Got expected number of animations on pseudo-element');
+  assert_equals(anims[0].transitionProperty, 'height');
+  assert_equals(anims[1].transitionProperty, 'opacity');
+  assert_equals(anims[2].transitionProperty, 'width');
+}, 'getAnimations sorts simultaneous transitions by name');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+  { "set": [["dom.animations-api.core.enabled", true]]},
+  function() {
+    window.open("file_pseudoElement-get-animations.html");
+  });
+</script>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -43,16 +43,18 @@ support-files = css-animations/file_cssa
 support-files = css-animations/file_document-get-animations.html
 [css-animations/test_effect-target.html]
 support-files = css-animations/file_effect-target.html
 [css-animations/test_element-get-animations.html]
 skip-if = buildapp == 'mulet'
 support-files = css-animations/file_element-get-animations.html
 [css-animations/test_keyframeeffect-getframes.html]
 support-files = css-animations/file_keyframeeffect-getframes.html
+[css-animations/test_pseudoElement-get-animations.html]
+support-files = css-animations/file_pseudoElement-get-animations.html
 [css-transitions/test_animation-cancel.html]
 support-files = css-transitions/file_animation-cancel.html
 [css-transitions/test_animation-computed-timing.html]
 support-files = css-transitions/file_animation-computed-timing.html
 [css-transitions/test_animation-currenttime.html]
 support-files = css-transitions/file_animation-currenttime.html
 [css-transitions/test_animation-finished.html]
 support-files = css-transitions/file_animation-finished.html
@@ -68,16 +70,18 @@ support-files = css-transitions/file_css
 support-files = css-transitions/file_document-get-animations.html
 [css-transitions/test_effect-target.html]
 support-files = css-transitions/file_effect-target.html
 [css-transitions/test_element-get-animations.html]
 skip-if = buildapp == 'mulet'
 support-files = css-transitions/file_element-get-animations.html
 [css-transitions/test_keyframeeffect-getframes.html]
 support-files = css-transitions/file_keyframeeffect-getframes.html
+[css-transitions/test_pseudoElement-get-animations.html]
+support-files = css-transitions/file_pseudoElement-get-animations.html
 [document-timeline/test_document-timeline.html]
 support-files = document-timeline/file_document-timeline.html
 [document-timeline/test_request_animation_frame.html]
 skip-if = buildapp == 'mulet'
 [mozilla/test_deferred_start.html]
 support-files = mozilla/file_deferred_start.html
 skip-if = (toolkit == 'gonk' && debug)
 [mozilla/test_hide_and_show.html]
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -25,16 +25,44 @@ function addDiv(t, attrs) {
         div.parentNode.removeChild(div);
       }
     });
   }
   return div;
 }
 
 /**
+ * Appends a style div to the document head.
+ *
+ * @param t  The testharness.js Test object. If provided, this will be used
+ *           to register a cleanup callback to remove the style element
+ *           when the test finishes.
+ *
+ * @param rules  A dictionary object with selector names and rules to set on
+ *               the style sheet.
+ */
+function addStyle(t, rules) {
+  var extraStyle = document.createElement('style');
+  document.head.appendChild(extraStyle);
+  if (rules) {
+    var sheet = extraStyle.sheet;
+    for (var selector in rules) {
+      sheet.insertRule(selector + '{' + rules[selector] + '}',
+                       sheet.cssRules.length);
+    }
+  }
+
+  if (t && typeof t.add_cleanup === 'function') {
+    t.add_cleanup(function() {
+      extraStyle.remove();
+    });
+  }
+}
+
+/**
  * Promise wrapper for requestAnimationFrame.
  */
 function waitForFrame() {
   return new Promise(function(resolve, reject) {
     window.requestAnimationFrame(resolve);
   });
 }
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -63,16 +63,17 @@
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "nsNodeUtils.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsDocument.h"
 #include "nsAttrValueOrString.h"
 #include "nsAttrValueInlines.h"
+#include "nsCSSPseudoElements.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif /* MOZ_XUL */
 #include "nsSVGElement.h"
 #include "nsFrameSelection.h"
 #ifdef DEBUG
 #include "nsRange.h"
 #endif
@@ -3356,25 +3357,31 @@ Element::Animate(JSContext* aContext,
 void
 Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
 {
   nsIDocument* doc = GetComposedDoc();
   if (doc) {
     doc->FlushPendingNotifications(Flush_Style);
   }
 
-  GetAnimationsUnsorted(aAnimations);
+  GetAnimationsUnsorted(this, CSSPseudoElementType::NotPseudo, aAnimations);
   aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
 }
 
-void
-Element::GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations)
+/* static */ void
+Element::GetAnimationsUnsorted(Element* aElement,
+                               CSSPseudoElementType aPseudoType,
+                               nsTArray<RefPtr<Animation>>& aAnimations)
 {
-  EffectSet* effects = EffectSet::GetEffectSet(this,
-                                               CSSPseudoElementType::NotPseudo);
+  MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo ||
+             aPseudoType == CSSPseudoElementType::after ||
+             aPseudoType == CSSPseudoElementType::before,
+             "Unsupported pseudo type");
+
+  EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
   if (!effects) {
     return;
   }
 
   for (KeyframeEffectReadOnly* effect : *effects) {
     MOZ_ASSERT(effect && effect->GetAnimation(),
                "Only effects associated with an animation should be "
                "added to an element's effect set");
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -113,16 +113,17 @@ enum {
 };
 
 #undef ELEMENT_FLAG_BIT
 
 // Make sure we have space for our bits
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
 
 namespace mozilla {
+enum class CSSPseudoElementType : uint8_t;
 class EventChainPostVisitor;
 class EventChainPreVisitor;
 class EventChainVisitor;
 class EventListenerManager;
 class EventStateManager;
 
 namespace dom {
 
@@ -828,17 +829,19 @@ public:
   already_AddRefed<Animation> Animate(
     JSContext* aContext,
     JS::Handle<JSObject*> aFrames,
     const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
     ErrorResult& aError);
 
   // Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
   void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
-  void GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations);
+  static void GetAnimationsUnsorted(Element* aElement,
+                                    CSSPseudoElementType aPseudoType,
+                                    nsTArray<RefPtr<Animation>>& aAnimations);
 
   NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
   virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   void GetOuterHTML(nsAString& aOuterHTML);
   void SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError);
   void InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
                           ErrorResult& aError);
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -117,25 +117,16 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
 
-#define ARRAY_SCRIPTABLE_FLAGS                                                \
-  (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
-   nsIXPCScriptable::WANT_GETPROPERTY |                                       \
-   nsIXPCScriptable::WANT_ENUMERATE)
-
-#define EVENTTARGET_SCRIPTABLE_FLAGS                                          \
-  (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
-   nsIXPCScriptable::WANT_ADDPROPERTY)
-
 #define DOMCLASSINFO_STANDARD_FLAGS                                           \
   (nsIClassInfo::MAIN_THREAD_ONLY |                                           \
    nsIClassInfo::DOM_OBJECT       |                                           \
    nsIClassInfo::SINGLETON_CLASSINFO)
 
 
 #ifdef DEBUG
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -26,16 +26,17 @@
 #include "prprf.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsUnicharUtils.h"
 #include "nsContentList.h"
+#include "nsCSSPseudoElements.h"
 #include "nsIObserver.h"
 #include "nsIBaseWindow.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/ImageLoader.h"
 #include "nsDocShell.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsCOMArray.h"
@@ -3135,31 +3136,30 @@ nsDocument::Timeline()
   return mDocumentTimeline;
 }
 
 void
 nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
 {
   FlushPendingNotifications(Flush_Style);
 
-  // Bug 1174575: Until we implement a suitable PseudoElement interface we
-  // don't have anything to return for the |target| attribute of
-  // KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
-  // Rather than return some half-baked version of these objects (e.g.
-  // we a null effect attribute) we simply don't provide access to animations
-  // whose effect refers to a pseudo-element until we can support them
-  // properly.
   for (nsIContent* node = nsINode::GetFirstChild();
        node;
        node = node->GetNextNode(this)) {
     if (!node->IsElement()) {
       continue;
     }
 
-    node->AsElement()->GetAnimationsUnsorted(aAnimations);
+    Element* element = node->AsElement();
+    Element::GetAnimationsUnsorted(element, CSSPseudoElementType::NotPseudo,
+                                   aAnimations);
+    Element::GetAnimationsUnsorted(element, CSSPseudoElementType::before,
+                                   aAnimations);
+    Element::GetAnimationsUnsorted(element, CSSPseudoElementType::after,
+                                   aAnimations);
   }
 
   // Sort animations by priority
   aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
 }
 
 /* Return true if the document is in the focused top-level window, and is an
  * ancestor of the focused DOMWindow. */
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -303,16 +303,17 @@ GK_ATOM(digit, "digit")
 GK_ATOM(dir, "dir")
 GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
 GK_ATOM(directionality, "directionality")
 GK_ATOM(directory, "directory")
 GK_ATOM(disableOutputEscaping, "disable-output-escaping")
 GK_ATOM(disabled, "disabled")
 GK_ATOM(disablehistory, "disablehistory")
 GK_ATOM(display, "display")
+GK_ATOM(displayMode, "display-mode")
 GK_ATOM(distinct, "distinct")
 GK_ATOM(div, "div")
 GK_ATOM(dl, "dl")
 GK_ATOM(doctypePublic, "doctype-public")
 GK_ATOM(doctypeSystem, "doctype-system")
 GK_ATOM(document, "document")
 GK_ATOM(download, "download")
 GK_ATOM(DOMAttrModified, "DOMAttrModified")
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -365,51 +365,57 @@ Request::Constructor(const GlobalObject&
     }
   }
 
   requestHeaders->Fill(*headers, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (aInit.mBody.WasPassed() || temporaryBody) {
+  if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) ||
+      temporaryBody) {
     // HEAD and GET are not allowed to have a body.
     nsAutoCString method;
     request->GetMethod(method);
     // method is guaranteed to be uppercase due to step 14.2 above.
     if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
       aRv.ThrowTypeError<MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD>();
       return nullptr;
     }
   }
 
   if (aInit.mBody.WasPassed()) {
-    const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
-    nsCOMPtr<nsIInputStream> stream;
-    nsAutoCString contentType;
-    aRv = ExtractByteStreamFromBody(bodyInit,
-                                    getter_AddRefs(stream), contentType);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-
-    temporaryBody = stream;
+    const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
+      aInit.mBody.Value();
+    if (!bodyInitNullable.IsNull()) {
+      const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
+        bodyInitNullable.Value();
+      nsCOMPtr<nsIInputStream> stream;
+      nsAutoCString contentType;
+      aRv = ExtractByteStreamFromBody(bodyInit,
+                                      getter_AddRefs(stream), contentType);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
 
-    if (!contentType.IsVoid() &&
-        !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
-      requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
-                             contentType, aRv);
-    }
+      temporaryBody = stream;
+
+      if (!contentType.IsVoid() &&
+          !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+        requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
+                               contentType, aRv);
+      }
 
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
+
+      request->ClearCreatedByFetchEvent();
+      request->SetBody(temporaryBody);
     }
-
-    request->ClearCreatedByFetchEvent();
-    request->SetBody(temporaryBody);
   }
 
   RefPtr<Request> domRequest = new Request(global, request);
   domRequest->SetMimeType();
 
   if (aInput.IsRequest()) {
     RefPtr<Request> inputReq = &aInput.GetAsRequest();
     nsCOMPtr<nsIInputStream> body;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -515,28 +515,31 @@ child:
      * content processes always render to a virtual <0, 0> top-left
      * point.
      */
     async Show(ScreenIntSize size,
                ShowInfo info,
                TextureFactoryIdentifier textureFactoryIdentifier,
                uint64_t layersId,
                nullable PRenderFrame renderFrame,
-               bool parentIsActive);
+               bool parentIsActive,
+               nsSizeMode sizeMode);
 
     async LoadURL(nsCString uri, BrowserConfiguration config, ShowInfo info);
 
     async OpenURI(URIParams uri, uint32_t flags);
 
     async CacheFileDescriptor(nsString path, FileDescriptor fd);
 
-    async UpdateDimensions(CSSRect rect, CSSSize size, nsSizeMode sizeMode,
+    async UpdateDimensions(CSSRect rect, CSSSize size,
                            ScreenOrientationInternal orientation,
                            LayoutDeviceIntPoint chromeDisp) compressall;
 
+    async SizeModeChanged(nsSizeMode sizeMode);
+
     /**
      * Sending an activate message moves focus to the child.
      */
     async Activate();
 
     async Deactivate();
 
     async ParentActivated(bool aActivated);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1477,17 +1477,17 @@ TabChild::CancelCachedFileDescriptorCall
 }
 
 void
 TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                      const uint64_t& aLayersId,
                      PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
 {
   RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier,
-           aLayersId, aRenderFrame, mParentIsActive);
+           aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal);
   mDidFakeShow = true;
 }
 
 void
 TabChild::ApplyShowInfo(const ShowInfo& aInfo)
 {
   if (mDidSetRealShowInfo) {
     return;
@@ -1589,20 +1589,22 @@ TabChild::MaybeRequestPreinitCamera()
 #endif
 
 bool
 TabChild::RecvShow(const ScreenIntSize& aSize,
                    const ShowInfo& aInfo,
                    const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                    const uint64_t& aLayersId,
                    PRenderFrameChild* aRenderFrame,
-                   const bool& aParentIsActive)
+                   const bool& aParentIsActive,
+                   const nsSizeMode& aSizeMode)
 {
     MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame));
 
+    mPuppetWidget->SetSizeMode(aSizeMode);
     if (mDidFakeShow) {
         ApplyShowInfo(aInfo);
         RecvParentActivated(aParentIsActive);
         return true;
     }
 
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
     if (!baseWindow) {
@@ -1628,17 +1630,16 @@ TabChild::RecvShow(const ScreenIntSize& 
     ApplyShowInfo(aInfo);
     RecvParentActivated(aParentIsActive);
 
     return res;
 }
 
 bool
 TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
-                               const nsSizeMode& sizeMode,
                                const ScreenOrientationInternal& orientation,
                                const LayoutDeviceIntPoint& chromeDisp)
 {
     if (!mRemoteFrame) {
         return true;
     }
 
     mUnscaledOuterRect = rect;
@@ -1655,25 +1656,40 @@ TabChild::RecvUpdateDimensions(const CSS
 
     // Set the size on the document viewer before we update the widget and
     // trigger a reflow. Otherwise the MobileViewportManager reads the stale
     // size from the content viewer when it computes a new CSS viewport.
     nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
     baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
                                 true);
 
-    mPuppetWidget->SetSizeMode(sizeMode);
     mPuppetWidget->Resize(screenRect.x + chromeDisp.x,
                           screenRect.y + chromeDisp.y,
                           screenSize.width, screenSize.height, true);
 
     return true;
 }
 
 bool
+TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode)
+{
+  mPuppetWidget->SetSizeMode(aSizeMode);
+  nsCOMPtr<nsIDocument> document(GetDocument());
+  nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+  if (presShell) {
+    nsPresContext* presContext = presShell->GetPresContext();
+    if (presContext) {
+      presContext->MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
+                                                         NS_STYLE_HINT_REFLOW);
+    }
+  }
+  return true;
+}
+
+bool
 TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   return TabChildBase::UpdateFrameHandler(aFrameMetrics);
 }
 
 bool
 TabChild::RecvSuppressDisplayport(const bool& aEnabled)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -321,24 +321,26 @@ public:
                                        override;
 
   virtual bool
   RecvShow(const ScreenIntSize& aSize,
            const ShowInfo& aInfo,
            const TextureFactoryIdentifier& aTextureFactoryIdentifier,
            const uint64_t& aLayersId,
            PRenderFrameChild* aRenderFrame,
-           const bool& aParentIsActive) override;
+           const bool& aParentIsActive,
+           const nsSizeMode& aSizeMode) override;
 
   virtual bool
   RecvUpdateDimensions(const CSSRect& aRect,
                        const CSSSize& aSize,
-                       const nsSizeMode& aSizeMode,
                        const ScreenOrientationInternal& aOrientation,
                        const LayoutDeviceIntPoint& aChromeDisp) override;
+  virtual bool
+  RecvSizeModeChanged(const nsSizeMode& aSizeMode) override;
 
   virtual bool RecvActivate() override;
 
   virtual bool RecvDeactivate() override;
 
   virtual bool RecvMouseEvent(const nsString& aType,
                               const float& aX,
                               const float& aY,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -274,16 +274,17 @@ TabParent::TabParent(nsIContentParent* a
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
   , mDefaultScale(0)
   , mUpdatedDimensions(false)
+  , mSizeMode(nsSizeMode_Normal)
   , mManager(aManager)
   , mDocShellIsActive(false)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mIsDetached(true)
   , mAppPackageFileDescriptorSent(false)
   , mSendOfflineStatus(true)
   , mChromeFlags(aChromeFlags)
@@ -889,29 +890,34 @@ TabParent::Show(const ScreenIntSize& siz
                                     &layersId,
                                     &success);
           MOZ_ASSERT(success);
           AddTabParentToTable(layersId, this);
           Unused << SendPRenderFrameConstructor(renderFrame);
         }
     }
 
+    nsCOMPtr<nsISupports> container = mFrameElement->OwnerDoc()->GetContainer();
+    nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+    nsCOMPtr<nsIWidget> mainWidget;
+    baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+    mSizeMode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
+
     Unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier,
-                       layersId, renderFrame, aParentIsActive);
+                       layersId, renderFrame, aParentIsActive, mSizeMode);
 }
 
 bool
 TabParent::RecvSetDimensions(const uint32_t& aFlags,
                              const int32_t& aX, const int32_t& aY,
                              const int32_t& aCx, const int32_t& aCy)
 {
   MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER),
              "We should never see DIM_FLAGS_SIZE_INNER here!");
 
-  nsCOMPtr<nsIWidget> widget = GetWidget();
   NS_ENSURE_TRUE(mFrameElement, true);
   nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
   NS_ENSURE_TRUE(docShell, true);
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner);
   NS_ENSURE_TRUE(treeOwnerAsWin, true);
 
@@ -985,22 +991,30 @@ TabParent::UpdateDimensions(const nsIntR
                                 PixelCastJustification::LayoutDeviceIsScreenForTabDims);
     LayoutDeviceIntSize devicePixelSize =
       ViewAs<LayoutDevicePixel>(mDimensions,
                                 PixelCastJustification::LayoutDeviceIsScreenForTabDims);
 
     CSSRect unscaledRect = devicePixelRect / widgetScale;
     CSSSize unscaledSize = devicePixelSize / widgetScale;
     Unused << SendUpdateDimensions(unscaledRect, unscaledSize,
-                                   widget->SizeMode(),
                                    orientation, chromeOffset);
   }
 }
 
 void
+TabParent::SizeModeChanged(const nsSizeMode& aSizeMode)
+{
+  if (!mIsDestroyed && aSizeMode != mSizeMode) {
+    mSizeMode = aSizeMode;
+    Unused << SendSizeModeChanged(aSizeMode);
+  }
+}
+
+void
 TabParent::UIResolutionChanged()
 {
   if (!mIsDestroyed) {
     // TryCacheDPIAndScale()'s cache is keyed off of
     // mDPI being greater than 0, so this invalidates it.
     mDPI = -1;
     TryCacheDPIAndScale();
     // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -332,16 +332,18 @@ public:
 
   // XXX/cjones: it's not clear what we gain by hiding these
   // message-sending functions under a layer of indirection and
   // eating the return values
   void Show(const ScreenIntSize& aSize, bool aParentIsActive);
 
   void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
 
+  void SizeModeChanged(const nsSizeMode& aSizeMode);
+
   void UIResolutionChanged();
 
   void ThemeChanged();
 
   void HandleAccessKey(nsTArray<uint32_t>& aCharCodes,
                        const bool& aIsTrusted,
                        const int32_t& aModifierMask);
 
@@ -595,16 +597,17 @@ protected:
   ContentCacheInParent mContentCache;
 
   nsIntRect mRect;
   ScreenIntSize mDimensions;
   ScreenOrientationInternal mOrientation;
   float mDPI;
   CSSToLayoutDeviceScale mDefaultScale;
   bool mUpdatedDimensions;
+  nsSizeMode mSizeMode;
   LayoutDeviceIntPoint mChromeOffset;
 
 private:
   void DestroyInternal();
 
   already_AddRefed<nsFrameLoader>
   GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
 
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -188,38 +188,30 @@ private:
 
 class FrameParser {
 public:
 
   // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
   const Frame& CurrentFrame() const { return mFrame; }
 
 
-#ifdef ENABLE_TESTS
-  // Returns the previously parsed frame. Reset via Reset.
-  const Frame& PrevFrame() const { return mPrevFrame; }
-#endif
-
   // Returns the first parsed frame. Reset via Reset.
   const Frame& FirstFrame() const { return mFirstFrame; }
 
   // Resets the parser. Don't use between frames as first frame data is reset.
   void Reset() {
     EndFrameSession();
     mFirstFrame.Reset();
   }
 
   // Clear the last parsed frame to allow for next frame parsing, i.e.:
   // - sets PrevFrame to CurrentFrame
   // - resets the CurrentFrame
   // - resets ID3Header if no valid header was parsed yet
   void EndFrameSession() {
-#ifdef ENABLE_TESTS
-    mPrevFrame = mFrame;
-#endif
     mFrame.Reset();
   }
 
   // Parses contents of given ByteReader for a valid frame header and returns true
   // if one was found. After returning, the variable passed to 'aBytesToSkip' holds
   // the amount of bytes to be skipped (if any) in order to jump across a large
   // ID3v2 tag spanning multiple buffers.
   bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
@@ -232,19 +224,16 @@ public:
     return found;
   }
 
 private:
   // We keep the first parsed frame around for static info access, the
   // previously parsed frame for debugging and the currently parsed frame.
   Frame mFirstFrame;
   Frame mFrame;
-#ifdef ENABLE_TESTS
-  Frame mPrevFrame;
-#endif
 };
 
 
 // Return the AAC Profile Level Indication based upon sample rate and channels
 // Information based upon table 1.10 from ISO/IEC 14496-3:2005(E)
 static int8_t
 ProfileLevelIndication(const Frame& frame)
 {
@@ -431,36 +420,16 @@ ADTSTrackDemuxer::Init()
   InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig);
 
   ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 "}",
           mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, mInfo->mDuration);
 
   return mSamplesPerSecond && mChannels;
 }
 
-#ifdef ENABLE_TESTS
-const adts::Frame&
-ADTSTrackDemuxer::LastFrame() const
-{
-  return mParser->PrevFrame();
-}
-
-RefPtr<MediaRawData>
-ADTSTrackDemuxer::DemuxSample()
-{
-  return GetNextFrame(FindNextFrame());
-}
-
-media::TimeUnit
-ADTSTrackDemuxer::SeekPosition() const
-{
-  return Duration(mFrameIndex);
-}
-#endif
-
 UniquePtr<TrackInfo>
 ADTSTrackDemuxer::GetInfo() const
 {
   return mInfo->Clone();
 }
 
 RefPtr<ADTSTrackDemuxer::SeekPromise>
 ADTSTrackDemuxer::Seek(media::TimeUnit aTime)
--- a/dom/media/ADTSDemuxer.h
+++ b/dom/media/ADTSDemuxer.h
@@ -54,22 +54,16 @@ public:
 
   // Returns the estimated stream duration, or a 0-duration if unknown.
   media::TimeUnit Duration() const;
 
   // Returns the estimated duration up to the given frame number,
   // or a 0-duration if unknown.
   media::TimeUnit Duration(int64_t aNumFrames) const;
 
-#ifdef ENABLE_TESTS
-  const adts::Frame& LastFrame() const;
-  RefPtr<MediaRawData> DemuxSample();
-  media::TimeUnit SeekPosition() const;
-#endif
-
   // MediaTrackDemuxer interface.
   UniquePtr<TrackInfo> GetInfo() const override;
   RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
   RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
   void Reset() override;
   RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
     media::TimeUnit aTimeThreshold) override;
   int64_t GetResourceOffset() const override;
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -152,27 +152,25 @@ media::TimeUnit
 MP3TrackDemuxer::SeekPosition() const {
   TimeUnit pos = Duration(mFrameIndex);
   if (Duration() > TimeUnit()) {
     pos = std::min(Duration(), pos);
   }
   return pos;
 }
 
-#ifdef ENABLE_TESTS
 const FrameParser::Frame&
 MP3TrackDemuxer::LastFrame() const {
   return mParser.PrevFrame();
 }
 
 RefPtr<MediaRawData>
 MP3TrackDemuxer::DemuxSample() {
   return GetNextFrame(FindNextFrame());
 }
-#endif
 
 const ID3Parser::ID3Header&
 MP3TrackDemuxer::ID3Header() const {
   return mParser.ID3Header();
 }
 
 const FrameParser::VBRHeader&
 MP3TrackDemuxer::VBRInfo() const {
@@ -656,33 +654,29 @@ FrameParser::Reset() {
 }
 
 void
 FrameParser::EndFrameSession() {
   if (!mID3Parser.Header().IsValid()) {
     // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
     mID3Parser.Reset();
   }
-#ifdef ENABLE_TESTS
   mPrevFrame = mFrame;
-#endif
   mFrame.Reset();
 }
 
 const FrameParser::Frame&
 FrameParser::CurrentFrame() const {
   return mFrame;
 }
 
-#ifdef ENABLE_TESTS
 const FrameParser::Frame&
 FrameParser::PrevFrame() const {
   return mPrevFrame;
 }
-#endif
 
 const FrameParser::Frame&
 FrameParser::FirstFrame() const {
   return mFirstFrame;
 }
 
 const ID3Parser::ID3Header&
 FrameParser::ID3Header() const {
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/MP3Demuxer.h
@@ -292,20 +292,18 @@ public:
   };
 
   // Constructor.
   FrameParser();
 
   // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
   const Frame& CurrentFrame() const;
 
-#ifdef ENABLE_TESTS
   // Returns the previously parsed frame. Reset via Reset.
   const Frame& PrevFrame() const;
-#endif
 
   // Returns the first parsed frame. Reset via Reset.
   const Frame& FirstFrame() const;
 
   // Returns the parsed ID3 header. Note: check for validity.
   const ID3Parser::ID3Header& ID3Header() const;
 
   // Returns the parsed VBR header info. Note: check for validity by type.
@@ -338,19 +336,17 @@ private:
 
   // VBR header parser.
   VBRHeader mVBRHeader;
 
   // We keep the first parsed frame around for static info access, the
   // previously parsed frame for debugging and the currently parsed frame.
   Frame mFirstFrame;
   Frame mFrame;
-#ifdef ENABLE_TESTS
   Frame mPrevFrame;
-#endif
 };
 
 // The MP3 demuxer used to extract MPEG frames and side information out of
 // MPEG streams.
 class MP3TrackDemuxer : public MediaTrackDemuxer {
 public:
   // Constructor, expecting a valid media resource.
   explicit MP3TrackDemuxer(MediaResource* aSource);
@@ -367,20 +363,18 @@ public:
 
   // Returns the estimated duration up to the given frame number,
   // or a 0-duration if unknown.
   media::TimeUnit Duration(int64_t aNumFrames) const;
 
   // Returns the estimated current seek position time.
   media::TimeUnit SeekPosition() const;
 
-#ifdef ENABLE_TESTS
   const FrameParser::Frame& LastFrame() const;
   RefPtr<MediaRawData> DemuxSample();
-#endif
 
   const ID3Parser::ID3Header& ID3Header() const;
   const FrameParser::VBRHeader& VBRInfo() const;
 
   // MediaTrackDemuxer interface.
   UniquePtr<TrackInfo> GetInfo() const override;
   RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
   RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
--- a/dom/media/wave/WaveDemuxer.cpp
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -115,17 +115,20 @@ WAVTrackDemuxer::Init()
     aChunkSize += aChunkSize % 2;
 
     if (aChunkName == FRMT_CODE) {
       if (!FmtChunkParserInit()) {
         return false;
       }
     } else if (aChunkName == LIST_CODE) {
       mHeaderParser.Reset();
-      uint32_t endOfListChunk = mOffset + aChunkSize;
+      uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + aChunkSize;
+      if (endOfListChunk > UINT32_MAX) {
+        return false;
+      }
       if (!ListChunkParserInit(aChunkSize)) {
         mOffset = endOfListChunk;
       }
     } else if (aChunkName == DATA_CODE) {
       mDataLength = aChunkSize;
       if (mFirstChunkOffset != mOffset) {
         mFirstChunkOffset = mOffset;
       }
@@ -229,19 +232,19 @@ WAVTrackDemuxer::ListChunkParserInit(uin
     }
 
     MediaByteRange mRange = { mOffset, mOffset + length };
     RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
     if (!mChunkData) {
       return false;
     }
 
-    const char* mRawData = reinterpret_cast<const char*>(mChunkData->Data());
+    const char* rawData = reinterpret_cast<const char*>(mChunkData->Data());
 
-    nsCString val(mRawData, length);
+    nsCString val(rawData, length);
     if (length > 0 && val[length - 1] == '\0') {
       val.SetLength(length - 1);
     }
 
     if (length % 2) {
       mOffset += 1;
       length += length % 2;
     }
@@ -401,47 +404,48 @@ int64_t
 WAVTrackDemuxer::StreamLength() const
 {
   return mSource.GetLength();
 }
 
 TimeUnit
 WAVTrackDemuxer::Duration() const
 {
-  if (!mDataLength) {
+  if (!mDataLength ||!mChannels || !mSampleFormat) {
     return TimeUnit();
   }
 
-  int64_t numSamples = mDataLength * 8 / mChannels / mSampleFormat;
+  int64_t numSamples =
+    static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat;
 
   int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
 
   if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
     numUSeconds++;
   }
 
   return TimeUnit::FromMicroseconds(numUSeconds);
 }
 
 TimeUnit
 WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
 {
-  if (!mSamplesPerSecond) {
+  if (!mSamplesPerSecond || !mSamplesPerChunk) {
     return TimeUnit();
   }
   const double usPerDataChunk = USECS_PER_S *
                                 static_cast<double>(mSamplesPerChunk) /
                                 mSamplesPerSecond;
   return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
 }
 
 TimeUnit
 WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
 {
-  if (!mSamplesPerSecond) {
+  if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
     return TimeUnit();
   }
 
   int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
 
   int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
 
   if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
@@ -582,16 +586,19 @@ WAVTrackDemuxer::ChunkIndexFromOffset(in
 {
   int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
   return std::max<int64_t>(0, chunkIndex);
 }
 
 int64_t
 WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
 {
+  if (!mSamplesPerChunk || !mSamplesPerSecond) {
+    return 0;
+  }
   int64_t chunkIndex =
     (aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
   return chunkIndex;
 }
 
 void
 WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
 {
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -261,16 +261,17 @@ nsPluginTag::nsPluginTag(const char* aNa
                          const char* const* aExtensions,
                          int32_t aVariants,
                          int64_t aLastModifiedTime,
                          bool fromExtension,
                          bool aArgsAreUTF8)
   : nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
     mId(sNextId++),
     mContentProcessRunningCount(0),
+    mHadLocalInstance(false),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
     mSupportsAsyncInit(false),
     mFullPath(aFullPath),
     mLastModifiedTime(aLastModifiedTime),
     mSandboxLevel(0),
     mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -200,21 +200,48 @@ CSPService::ShouldProcess(uint32_t      
                           nsIURI           *aContentLocation,
                           nsIURI           *aRequestOrigin,
                           nsISupports      *aRequestContext,
                           const nsACString &aMimeTypeGuess,
                           nsISupports      *aExtra,
                           nsIPrincipal     *aRequestPrincipal,
                           int16_t          *aDecision)
 {
-  if (!aContentLocation)
+  if (!aContentLocation) {
     return NS_ERROR_FAILURE;
+  }
+
+  if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
+    nsAutoCString location;
+    aContentLocation->GetSpec(location);
+    MOZ_LOG(gCspPRLog, LogLevel::Debug,
+        ("CSPService::ShouldProcess called for %s", location.get()));
+  }
 
-  *aDecision = nsIContentPolicy::ACCEPT;
-  return NS_OK;
+  // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
+  // internal contentPolicyType to the mapping external one.
+  // If it is not TYPE_OBJECT, we can return at this point.
+  // Note that we should still pass the internal contentPolicyType
+  // (aContentType) to ShouldLoad().
+  uint32_t policyType =
+    nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
+
+  if (policyType != nsIContentPolicy::TYPE_OBJECT) {
+    *aDecision = nsIContentPolicy::ACCEPT;
+    return NS_OK;
+  }
+
+  return ShouldLoad(aContentType,
+                    aContentLocation,
+                    aRequestOrigin,
+                    aRequestContext,
+                    aMimeTypeGuess,
+                    aExtra,
+                    aRequestPrincipal,
+                    aDecision);
 }
 
 /* nsIChannelEventSink implementation */
 NS_IMETHODIMP
 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
                                    nsIChannel *newChannel,
                                    uint32_t flags,
                                    nsIAsyncVerifyRedirectCallback *callback)
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_shouldprocess.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Helper for Test Bug 908933</title>
+    <meta charset="utf-8">
+  </head>
+  <body>
+	<object type="application/x-java-test" codebase="test1"></object>
+
+	<object classid="java:test2" codebase="./test2"></object>
+
+	<object data="test3" classid="java:test3" codebase="./test3"></object>
+
+	<applet codebase="test4"></applet>
+
+	<embed src="test5.class" codebase="test5" type="application/x-java-test">
+
+	<embed type="application/x-java-test" codebase="test6">
+
+	<embed src="test7.class">
+
+	<embed src="test8.class" codebase="test8">
+
+  </body>
+</html>
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -76,16 +76,17 @@ support-files =
   file_bug910139.xsl
   file_bug909029_star.html
   file_bug909029_star.html^headers^
   file_bug909029_none.html
   file_bug909029_none.html^headers^
   file_policyuri_regression_from_multipolicy.html
   file_policyuri_regression_from_multipolicy.html^headers^
   file_policyuri_regression_from_multipolicy_policy
+  file_shouldprocess.html
   file_nonce_source.html
   file_nonce_source.html^headers^
   file_bug941404.html
   file_bug941404_xhr.html
   file_bug941404_xhr.html^headers^
   file_hash_source.html
   file_dual_header_testserver.sjs
   file_hash_source.html^headers^
@@ -217,15 +218,18 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_upgrade_insecure_cors.html]
 skip-if = buildapp == 'b2g' || toolkit == 'gonk' || toolkit == 'android'
 [test_report_for_import.html]
 [test_blocked_uri_in_reports.html]
 [test_service_worker.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_child-src_worker.html]
 skip-if = buildapp == 'b2g' #investigate in bug 1222904
+[test_shouldprocess.html]
+# Fennec platform does not support Java applet plugin
+skip-if = toolkit == 'android' #investigate in bug 1250814
 [test_child-src_worker_data.html]
 [test_child-src_worker-redirect.html]
 [test_child-src_iframe.html]
 [test_meta_element.html]
 [test_meta_header_dual.html]
 [test_docwrite_meta.html]
 [test_multipartchannel.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_shouldprocess.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=908933
+-->
+<head>
+  <title>Test Bug 908933</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load variations of 'objects' and make sure all the
+ * resource loads are correctly blocked by CSP.
+ * For all the testing we use a CSP with "object-src 'none'"
+ * so that all the loads are either blocked by
+ * shouldProcess or shouldLoad.
+ */
+
+const POLICY = "default-src http://mochi.test:8888; object-src 'none'";
+const TESTFILE = "tests/dom/security/test/csp/file_shouldprocess.html";
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+  // Note that the files listed below don't actually exist.
+  // Since loading of them should be blocked by shouldProcess, we don't
+  // really need these files.
+
+  // blocked by shouldProcess
+  "http://mochi.test:8888/tests/dom/security/test/csp/test1",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test2",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test3",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test4",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test5",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test6",
+  // blocked by shouldLoad
+  "http://mochi.test:8888/tests/dom/security/test/csp/test7.class",
+  "http://mochi.test:8888/tests/dom/security/test/csp/test8.class",
+];
+
+function checkResults(aURI) {
+  var index = tests.indexOf(aURI);
+  if (index > -1) {
+    tests.splice(index, 1);
+    ok(true, "ShouldLoad or ShouldProcess blocks TYPE_OBJECT with uri: " + aURI + "!");
+  }
+  else {
+    ok(false, "ShouldLoad or ShouldProcess incorreclty blocks TYPE_OBJECT with uri: " + aURI + "!");
+  }
+  if (tests.length == 0) {
+    window.examiner.remove();
+    SimpleTest.finish();
+  }
+}
+
+// used to watch that shouldProcess blocks TYPE_OBJECT
+function examiner() {
+  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    if (topic === "csp-on-violate-policy") {
+      var asciiSpec =
+        SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+      checkResults(asciiSpec);
+    }
+  },
+  remove: function() {
+    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+  }
+}
+window.examiner = new examiner();
+
+function loadFrame() {
+  var src = "file_testserver.sjs";
+  // append the file that should be served
+  src += "?file=" + escape(TESTFILE);
+  // append the CSP that should be used to serve the file
+  src += "&csp=" + escape(POLICY);
+
+  var iframe = document.createElement("iframe");
+  iframe.src = src;
+  document.body.appendChild(iframe);
+}
+
+SpecialPowers.pushPrefEnv(
+  { "set": [['plugin.java.mime', 'application/x-java-test']] },
+  loadFrame);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -14,17 +14,17 @@ typedef unsigned long nsContentPolicyTyp
  Exposed=(Window,Worker)]
 interface Request {
   readonly attribute ByteString method;
   readonly attribute USVString url;
   [SameObject] readonly attribute Headers headers;
 
   [Func="mozilla::dom::Request::RequestContextEnabled"]
   readonly attribute RequestContext context;
-  readonly attribute DOMString referrer;
+  readonly attribute USVString referrer;
   readonly attribute RequestMode mode;
   readonly attribute RequestCredentials credentials;
   [Func="mozilla::dom::Request::RequestCacheEnabled"]
   readonly attribute RequestCache cache;
   readonly attribute RequestRedirect redirect;
 
   [Throws,
    NewObject] Request clone();
@@ -33,17 +33,17 @@ interface Request {
   [ChromeOnly]
   void setContentPolicyType(nsContentPolicyType context);
 };
 Request implements Body;
 
 dictionary RequestInit {
   ByteString method;
   HeadersInit headers;
-  BodyInit body;
+  BodyInit? body;
   RequestMode mode;
   RequestCredentials credentials;
   RequestCache cache;
   RequestRedirect redirect;
 };
 
 // Gecko currently does not ship RequestContext, so please don't use it in IDL
 // that is exposed to script.
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1237,17 +1237,17 @@ public:
     mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
     mRegistration->NotifyListenersOnChange();
     swm->StoreRegistration(mPrincipal, mRegistration);
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
 
     Done(NS_OK);
     // Activate() is invoked out of band of atomic.
-    mRegistration->TryToActivate();
+    mRegistration->TryToActivateAsync();
   }
 
 private:
   const InstallType mType;
 };
 
 class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase,
                                        public serviceWorkerScriptCache::CompareCallback
@@ -1849,19 +1849,33 @@ ServiceWorkerManager::AppendPendingOpera
 
   if (!mShuttingDown) {
     PendingOperation* opt = mPendingOperations.AppendElement();
     opt->mRunnable = aRunnable;
   }
 }
 
 void
+ServiceWorkerRegistrationInfo::TryToActivateAsync()
+{
+  nsCOMPtr<nsIRunnable> r =
+  NS_NewRunnableMethod(this,
+                       &ServiceWorkerRegistrationInfo::TryToActivate);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+}
+
+/*
+ * TryToActivate should not be called directly, use TryToACtivateAsync instead.
+ */
+void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
-  if (!IsControllingDocuments() || mWaitingWorker->SkipWaitingFlag()) {
+  if (!IsControllingDocuments() ||
+      // Waiting worker will be removed if the registration is removed
+      (mWaitingWorker && mWaitingWorker->SkipWaitingFlag())) {
     Activate();
   }
 }
 
 void
 ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess)
 {
   mRegistration->FinishActivate(aSuccess);
@@ -3404,17 +3418,17 @@ ServiceWorkerManager::StopControllingADo
     } else {
       // If the registration has an active worker that is running
       // this might be a good time to stop it.
       if (aRegistration->mActiveWorker) {
         ServiceWorkerPrivate* serviceWorkerPrivate =
           aRegistration->mActiveWorker->WorkerPrivate();
         serviceWorkerPrivate->NoteStoppedControllingDocuments();
       }
-      aRegistration->TryToActivate();
+      aRegistration->TryToActivateAsync();
     }
   }
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
                                      const nsAString& aUrl, nsAString& aScope)
 {
@@ -4185,17 +4199,17 @@ ServiceWorkerManager::SetSkipWaitingFlag
 
   if (registration->mInstallingWorker &&
       (registration->mInstallingWorker->ID() == aServiceWorkerID)) {
     registration->mInstallingWorker->SetSkipWaitingFlag();
   } else if (registration->mWaitingWorker &&
              (registration->mWaitingWorker->ID() == aServiceWorkerID)) {
     registration->mWaitingWorker->SetSkipWaitingFlag();
     if (registration->mWaitingWorker->State() == ServiceWorkerState::Installed) {
-      registration->TryToActivate();
+      registration->TryToActivateAsync();
     }
   } else {
     NS_WARNING("Failed to set skipWaiting flag, no matching worker.");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -135,16 +135,19 @@ public:
 
   void
   Clear();
 
   void
   PurgeActiveWorker();
 
   void
+  TryToActivateAsync();
+
+  void
   TryToActivate();
 
   void
   Activate();
 
   void
   FinishActivate(bool aSuccess);
 
--- a/dom/workers/test/serviceworkers/test_claim_oninstall.html
+++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html
@@ -33,23 +33,28 @@
     ok(registration.installing, "Worker should be in installing state");
 
     navigator.serviceWorker.oncontrollerchange = function() {
       ok(false, "Claim should not succeed when the worker is not active.");
     }
 
     var p = new Promise(function(res, rej) {
       registration.installing.onstatechange = function(e) {
+        ok(registration.waiting, "Worker should be in waitinging state");
+
         // The worker will become active only if claim will reject inside the
         // install handler.
+        registration.waiting.onstatechange = function(e) {
+          ok(registration.active, "Claim should reject if the worker is not active");
+          ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
+          e.target.onstatechange = null;
+          res();
+        }
 
-        ok(registration.active, "Claim should reject if the worker is not active");
-        ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
         e.target.onstatechange = null;
-        res();
       }
     });
 
     return p;
   }
 
   function runTest() {
     register()
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -137,17 +137,18 @@ JoinStyleToSkiaJoin(JoinStyle aJoin)
   return SkPaint::kDefault_Join;
 }
 
 static inline bool
 StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
 {
   // Skia renders 0 width strokes with a width of 1 (and in black),
   // so we should just skip the draw call entirely.
-  if (!aOptions.mLineWidth) {
+  // Skia does not handle non-finite line widths.
+  if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
     return false;
   }
   aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
   aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
   aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
   aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
 
   if (aOptions.mDashLength > 0) {
--- a/gfx/2d/PathSkia.cpp
+++ b/gfx/2d/PathSkia.cpp
@@ -185,17 +185,19 @@ PathSkia::StrokeContainsPoint(const Stro
                               const Point &aPoint,
                               const Matrix &aTransform) const
 {
   if (!mPath.isFinite()) {
     return false;
   }
 
   SkPaint paint;
-  StrokeOptionsToPaint(paint, aStrokeOptions);
+  if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+    return false;
+  }
 
   SkPath strokePath;
   paint.getFillPath(mPath, &strokePath);
 
   return SkPathContainsPoint(strokePath, aPoint, aTransform);
 }
 
 Rect
@@ -213,18 +215,20 @@ Rect
 PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
                            const Matrix &aTransform) const
 {
   if (!mPath.isFinite()) {
     return Rect();
   }
 
   SkPaint paint;
-  StrokeOptionsToPaint(paint, aStrokeOptions);
-  
+  if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+    return Rect();
+  }
+
   SkPath result;
   paint.getFillPath(mPath, &result);
 
   Rect bounds = SkRectToRect(result.getBounds());
   return aTransform.TransformBounds(bounds);
 }
 
 void
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -1,20 +1,25 @@
 /* -*- Mode: C++; tab-width: 20; 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 "BufferTexture.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/fallible.h"
 
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
 namespace mozilla {
 namespace layers {
 
 class MemoryTextureData : public BufferTextureData
 {
 public:
   static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                    gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
@@ -78,26 +83,46 @@ public:
   virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
 
   virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
 
 protected:
   mozilla::ipc::Shmem mShmem;
 };
 
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+  return gfxPlatformGtk::GetPlatform()->UseXRender();
+#endif
+  return false;
+}
+
+static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+                                         LayersBackend aLayersBackend)
+{
+  return aLayersBackend != LayersBackend::LAYERS_BASIC
+      || UsingX11Compositor()
+      || aFormat == gfx::SurfaceFormat::UNKNOWN
+      || aFormat == gfx::SurfaceFormat::YUV
+      || aFormat == gfx::SurfaceFormat::NV12;
+}
+
 BufferTextureData*
 BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                           gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
                           TextureAllocationFlags aAllocFlags,
                           ISurfaceAllocator* aAllocator)
 {
   if (!aAllocator || aAllocator->IsSameProcess()) {
-    return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+    return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+                                     aAllocFlags, aAllocator);
   } else {
-    return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+    return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+                                    aAllocFlags, aAllocator);
   }
 }
 
 BufferTextureData*
 BufferTextureData::CreateInternal(ISurfaceAllocator* aAllocator,
                                   const BufferDescriptor& aDesc,
                                   gfx::BackendType aMoz2DBackend,
                                   int32_t aBufferSize,
@@ -174,16 +199,26 @@ BufferTextureData::GetSize() const
 }
 
 gfx::SurfaceFormat
 BufferTextureData::GetFormat() const
 {
   return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
 }
 
+
+bool
+BufferTextureData::HasIntermediateBuffer() const
+{
+  if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+    return true;
+  }
+  return mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
+}
+
 bool
 BufferTextureData::SupportsMoz2D() const
 {
   switch (GetFormat()) {
     case gfx::SurfaceFormat::YUV:
     case gfx::SurfaceFormat::NV12:
     case gfx::SurfaceFormat::UNKNOWN:
       return false;
@@ -380,17 +415,17 @@ static bool InitBuffer(uint8_t* buf, siz
 
   return true;
 }
 
 MemoryTextureData*
 MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                           gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
                           TextureAllocationFlags aAllocFlags,
-                          ISurfaceAllocator*)
+                          ISurfaceAllocator* aAllocator)
 {
   // Should have used CreateForYCbCr.
   MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
 
   if (aSize.width <= 0 || aSize.height <= 0) {
     gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
     return nullptr;
   }
@@ -400,19 +435,24 @@ MemoryTextureData::Create(gfx::IntSize a
     return nullptr;
   }
 
   uint8_t* buf = new (fallible) uint8_t[bufSize];
   if (!InitBuffer(buf, bufSize, aAllocFlags)) {
     return nullptr;
   }
 
+  auto fwd = aAllocator ? aAllocator->AsCompositableForwarder() : nullptr;
+  bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+                                              fwd->GetCompositorBackendType())
+                                   : true;
+
   GfxMemoryImageReporter::DidAlloc(buf);
 
-  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
 
   return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
 }
 
 void
 MemoryTextureData::Deallocate(ISurfaceAllocator*)
 {
   MOZ_ASSERT(mBuffer);
@@ -472,17 +512,22 @@ ShmemTextureData::Create(gfx::IntSize aS
     return nullptr;
   }
 
   uint8_t* buf = shm.get<uint8_t>();
   if (!InitBuffer(buf, bufSize, aAllocFlags)) {
     return nullptr;
   }
 
-  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+  auto fwd = aAllocator->AsCompositableForwarder();
+  bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+                                              fwd->GetCompositorBackendType())
+                                   : true;
+
+  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
 
   return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
 
   return nullptr;
 }
 
 TextureData*
 ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -50,17 +50,17 @@ public:
   virtual bool CanExposeMappedData() const override { return true; }
 
   virtual bool BorrowMappedData(MappedTextureData& aMap) override;
 
   virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
 
   virtual bool SupportsMoz2D() const override;
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasIntermediateBuffer() const override;
 
   // use TextureClient's default implementation
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
   // Don't use this.
   void SetDesciptor(const BufferDescriptor& aDesc);
 
 protected:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -107,16 +107,17 @@
  */
 
 class nsIWidget;
 
 namespace mozilla {
 namespace gfx {
 class Matrix;
 class DrawTarget;
+class DataSourceSurface;
 } // namespace gfx
 
 namespace layers {
 
 struct Effect;
 struct EffectChain;
 class Image;
 class ImageHostOverlay;
@@ -187,16 +188,20 @@ public:
     : mCompositorID(0)
     , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
     , mParent(aParent)
     , mScreenRotation(ROTATION_0)
   {
   }
 
   virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
   virtual bool Initialize() = 0;
   virtual void Destroy() = 0;
 
   /**
    * Return true if the effect type is supported.
    *
    * By default Compositor implementations should support all effects but in
    * some rare cases it is not possible to support an effect efficiently.
--- a/gfx/layers/TextureDIB.h
+++ b/gfx/layers/TextureDIB.h
@@ -25,17 +25,17 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool SupportsMoz2D() const override { return true; }
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasIntermediateBuffer() const override { return true; }
 
   static
   DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                          ISurfaceAllocator* aAllocator);
 
 protected:
   DIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                  gfxWindowsSurface* aSurface)
@@ -75,17 +75,17 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
-  virtual bool HasInternalBuffer() const { return true; }
+  virtual bool HasIntermediateBuffer() const { return true; }
 
   virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
 
 protected:
   RefPtr<DataTextureSource> mTextureSource;
   RefPtr<Compositor> mCompositor;
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -31,46 +31,63 @@ using namespace mozilla::gfx;
 namespace layers {
 
 class DataTextureSourceBasic : public DataTextureSource
                              , public TextureSourceBasic
 {
 public:
   virtual const char* Name() const override { return "DataTextureSourceBasic"; }
 
+  explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+  : mSurface(aSurface)
+  , mWrappingExistingData(!!aSurface)
+  {}
+
+  virtual DataTextureSource* AsDataTextureSource() override
+  {
+    // If the texture wraps someone else's memory we'd rather not use it as
+    // a DataTextureSource per say (that is call Update on it).
+    return mWrappingExistingData ? nullptr : this;
+  }
+
   virtual TextureSourceBasic* AsSourceBasic() override { return this; }
 
   virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
 
   SurfaceFormat GetFormat() const override
   {
-    return mSurface->GetFormat();
+    return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
   }
 
   virtual IntSize GetSize() const override
   {
-    return mSurface->GetSize();
+    return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
   }
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override
   {
+    MOZ_ASSERT(!mWrappingExistingData);
+    if (mWrappingExistingData) {
+      return false;
+    }
     mSurface = aSurface;
     return true;
   }
 
   virtual void DeallocateDeviceData() override
   {
     mSurface = nullptr;
     SetUpdateSerial(0);
   }
 
 public:
   RefPtr<gfx::DataSourceSurface> mSurface;
+  bool mWrappingExistingData;
 };
 
 BasicCompositor::BasicCompositor(nsIWidget *aWidget)
   : mWidget(aWidget)
   , mDidExternalComposition(false)
 {
   MOZ_COUNT_CTOR(BasicCompositor);
 
@@ -177,17 +194,24 @@ BasicCompositor::CreateRenderTargetForWi
   }
 
   return rt.forget();
 }
 
 already_AddRefed<DataTextureSource>
 BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
 {
-  RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
+  RefPtr<DataTextureSource> result = new DataTextureSourceBasic(nullptr);
+  return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+  RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
   return result.forget();
 }
 
 bool
 BasicCompositor::SupportsEffect(EffectTypes aEffect)
 {
   return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
 }
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -65,16 +65,19 @@ public:
   virtual already_AddRefed<CompositingRenderTarget>
   CreateRenderTargetForWindow(const gfx::IntRect& aRect,
                               SurfaceInitMode aInit,
                               BufferMode aBufferMode);
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool SupportsEffect(EffectTypes aEffect) override;
 
   virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
   {
     mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
     mRenderTarget->BindRenderTarget();
   }
   virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -28,17 +28,17 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
 
   virtual bool SupportsMoz2D() const override { return true; }
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual void Deallocate(ISurfaceAllocator*) override;
 
   virtual TextureData*
   CreateSimilar(ISurfaceAllocator* aAllocator,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -47,15 +47,18 @@ class X11BasicCompositor : public BasicC
 {
 public:
 
   explicit X11BasicCompositor(nsIWidget *aWidget) : BasicCompositor(aWidget) {}
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
   virtual void EndFrame() override;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -285,29 +285,16 @@ CompositableClient::DumpTextureClient(st
     aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
   } else {
     aStream << gfxUtils::GetAsDataURI(dSurf).get();
   }
 }
 
 AutoRemoveTexture::~AutoRemoveTexture()
 {
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   if (mCompositable && mTexture && mCompositable->IsConnected()) {
-    // remove old buffer from CompositableHost
-    RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
-    RefPtr<AsyncTransactionTracker> tracker =
-        new RemoveTextureFromCompositableTracker(waiter);
-    // Hold TextureClient until transaction complete.
-    tracker->SetTextureClient(mTexture);
-    mTexture->SetRemoveFromCompositableWaiter(waiter);
-    // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
-    mCompositable->GetForwarder()->RemoveTextureFromCompositableAsync(tracker, mCompositable, mTexture);
-  }
-#else
-  if (mCompositable && mTexture) {
+    mTexture->RemoveFromCompositable(mCompositable);
     mCompositable->RemoveTexture(mTexture);
   }
-#endif
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -443,41 +443,23 @@ ContentClientDoubleBuffered::DestroyFron
 
 void
 ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
                                      const nsIntRegion& aVisibleRegion,
                                      bool aDidSelfCopy)
 {
   ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
 
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   if (mFrontClient) {
-    // remove old buffer from CompositableHost
-    RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
-    RefPtr<AsyncTransactionTracker> tracker =
-        new RemoveTextureFromCompositableTracker(waiter);
-    // Hold TextureClient until transaction complete.
-    tracker->SetTextureClient(mFrontClient);
-    mFrontClient->SetRemoveFromCompositableWaiter(waiter);
-    // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
-    GetForwarder()->RemoveTextureFromCompositableAsync(tracker, this, mFrontClient);
+    mFrontClient->RemoveFromCompositable(this);
   }
 
   if (mFrontClientOnWhite) {
-    // remove old buffer from CompositableHost
-    RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
-    RefPtr<AsyncTransactionTracker> tracker =
-        new RemoveTextureFromCompositableTracker(waiter);
-    // Hold TextureClient until transaction complete.
-    tracker->SetTextureClient(mFrontClientOnWhite);
-    mFrontClientOnWhite->SetRemoveFromCompositableWaiter(waiter);
-    // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
-    GetForwarder()->RemoveTextureFromCompositableAsync(tracker, this, mFrontClientOnWhite);
+    mFrontClientOnWhite->RemoveFromCompositable(this);
   }
-#endif
 }
 
 void
 ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
 {
   mFrontUpdatedRegion = aFrontUpdatedRegion;
 
   RefPtr<TextureClient> oldBack = mTextureClient;
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -198,17 +198,17 @@ ClientSingleTiledLayerBuffer::PaintThebe
   // The new buffer is now validated, remove the dirty region from it.
   mTile.mInvalidBack.SubOut(tileDirtyRegion);
 
   dt = nullptr;
 
   mTile.Flip();
   UnlockTile(mTile);
 
-  if (backBuffer->HasInternalBuffer()) {
+  if (backBuffer->HasIntermediateBuffer()) {
     // If our new buffer has an internal buffer, we don't want to keep another
     // TextureClient around unnecessarily, so discard the back-buffer.
     mTile.DiscardBackBuffer();
   }
 
   mValidRegion = aNewValidRegion;
   mLastPaintSurfaceMode = mode;
   mLastPaintContentType = content;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -422,20 +422,20 @@ TextureClient::Unlock()
   }
 
   mData->Unlock();
   mIsLocked = false;
   mOpenMode = OpenMode::OPEN_NONE;
 }
 
 bool
-TextureClient::HasInternalBuffer() const
+TextureClient::HasIntermediateBuffer() const
 {
   MOZ_ASSERT(IsValid());
-  return mData->HasInternalBuffer();
+  return mData->HasIntermediateBuffer();
 }
 
 gfx::IntSize
 TextureClient::GetSize() const
 {
   MOZ_ASSERT(IsValid());
   return mData->GetSize();
 }
@@ -947,16 +947,40 @@ bool TextureClient::CopyToTextureClient(
   RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
   destinationTarget->CopySurface(source,
                                  aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
                                  aPoint ? *aPoint : gfx::IntPoint(0, 0));
   return true;
 }
 
 void
+TextureClient::RemoveFromCompositable(CompositableClient* aCompositable,
+                                      AsyncTransactionWaiter* aWaiter)
+{
+  MOZ_ASSERT(aCompositable);
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  if (mActor && aCompositable->GetIPDLActor()
+      && mData->AsGrallocTextureData()) {
+    // remove old buffer from CompositableHost
+    RefPtr<AsyncTransactionWaiter> waiter = aWaiter ? aWaiter
+                                                    : new AsyncTransactionWaiter();
+    RefPtr<AsyncTransactionTracker> tracker =
+        new RemoveTextureFromCompositableTracker(waiter);
+    // Hold TextureClient until transaction complete.
+    tracker->SetTextureClient(this);
+    mRemoveFromCompositableWaiter = waiter;
+    // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
+    mActor->GetForwarder()->RemoveTextureFromCompositableAsync(tracker, aCompositable, this);
+  }
+#endif
+
+}
+
+void
 TextureClient::SetRemoveFromCompositableWaiter(AsyncTransactionWaiter* aWaiter) {
   mRemoveFromCompositableWaiter = aWaiter;
 }
 
 void
 TextureClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -42,16 +42,17 @@ namespace mozilla {
 namespace gl {
 class SharedSurface_Gralloc;
 }
 
 namespace layers {
 
 class AsyncTransactionWaiter;
 class CompositableForwarder;
+class GrallocTextureData;
 class ISurfaceAllocator;
 class CompositableClient;
 struct PlanarYCbCrData;
 class Image;
 class PTextureChild;
 class TextureChild;
 class TextureData;
 struct RawTextureBuffer;
@@ -187,17 +188,17 @@ public:
   virtual bool Lock(OpenMode aMode, FenceHandle* aFence) = 0;
 
   virtual void Unlock() = 0;
 
   virtual bool SupportsMoz2D() const { return false; }
 
   virtual bool CanExposeMappedData() const { return false; }
 
-  virtual bool HasInternalBuffer() const = 0;
+  virtual bool HasIntermediateBuffer() const = 0;
 
   virtual bool HasSynchronization() const { return false; }
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
 
   virtual bool BorrowMappedData(MappedTextureData&) { return false; }
 
   virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
@@ -226,16 +227,18 @@ public:
 
   virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
 
 #ifdef XP_WIN
   virtual D3D11TextureData* AsD3D11TextureData() {
     return nullptr;
   }
 #endif
+
+  virtual GrallocTextureData* AsGrallocTextureData() { return nullptr; }
 };
 
 /**
  * TextureClient is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * content-side half of a TextureClient/TextureHost pair. A corresponding
  * TextureHost lives on the compositor-side.
  *
@@ -405,17 +408,17 @@ public:
    */
   bool HasSynchronization() const { return false; }
 
   /**
    * Indicates whether the TextureClient implementation is backed by an
    * in-memory buffer. The consequence of this is that locking the
    * TextureClient does not contend with locking the texture on the host side.
    */
-  bool HasInternalBuffer() const;
+  bool HasIntermediateBuffer() const;
 
   /**
    * Allocate and deallocate a TextureChild actor.
    *
    * TextureChild is an implementation detail of TextureClient that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPextureChild and DeallocPTextureChild).
@@ -572,16 +575,19 @@ public:
 
   ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
   void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
 
   /// If you add new code that uses this method, you are probably doing something wrong.
   TextureData* GetInternalData() { return mData; }
   const TextureData* GetInternalData() const { return mData; }
 
+  virtual void RemoveFromCompositable(CompositableClient* aCompositable,
+                                      AsyncTransactionWaiter* aWaiter = nullptr);
+
 private:
   static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
 
   /**
    * Called once, during the destruction of the Texture, on the thread in which
    * texture's reference count reaches 0 (could be any thread).
    *
    * Here goes the shut-down code that uses virtual methods.
--- a/gfx/layers/client/TextureClientSharedSurface.h
+++ b/gfx/layers/client/TextureClientSharedSurface.h
@@ -39,17 +39,17 @@ protected:
 public:
 
   ~SharedSurfaceTextureData();
 
   virtual bool Lock(OpenMode, FenceHandle*) override { return false; }
 
   virtual void Unlock() override {}
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual gfx::SurfaceFormat GetFormat() const override {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
   virtual gfx::IntSize GetSize() const override;
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -564,32 +564,25 @@ TileClient::Dump(std::stringstream& aStr
     aStream << " fbow=" << mFrontBufferOnWhite.get();
   }
   aStream << ")";
 }
 
 void
 TileClient::Flip()
 {
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
-  if (mFrontBuffer && mFrontBuffer->GetIPDLActor() &&
-      mCompositableClient && mCompositableClient->GetIPDLActor()) {
-    // remove old buffer from CompositableHost
-    RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
-    RefPtr<AsyncTransactionTracker> tracker =
-        new RemoveTextureFromCompositableTracker(waiter);
-    // Hold TextureClient until transaction complete.
-    tracker->SetTextureClient(mFrontBuffer);
-    mFrontBuffer->SetRemoveFromCompositableWaiter(waiter);
-    // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
-    mManager->AsShadowForwarder()->RemoveTextureFromCompositableAsync(tracker,
-                                                                      mCompositableClient,
-                                                                      mFrontBuffer);
+  if (mCompositableClient) {
+    if (mFrontBuffer) {
+      mFrontBuffer->RemoveFromCompositable(mCompositableClient);
+    }
+    if (mFrontBufferOnWhite) {
+      mFrontBufferOnWhite->RemoveFromCompositable(mCompositableClient);
+    }
   }
-#endif
+
   RefPtr<TextureClient> frontBuffer = mFrontBuffer;
   RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
   mFrontBuffer = mBackBuffer;
   mFrontBufferOnWhite = mBackBufferOnWhite;
   mBackBuffer.Set(this, frontBuffer);
   mBackBufferOnWhite = frontBufferOnWhite;
   RefPtr<gfxSharedReadLock> frontLock = mFrontLock;
   mFrontLock = mBackLock;
@@ -662,35 +655,24 @@ TileClient::ValidateBackBufferFromFront(
   }
 }
 
 void
 TileClient::DiscardFrontBuffer()
 {
   if (mFrontBuffer) {
     MOZ_ASSERT(mFrontLock);
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
-    MOZ_ASSERT(!mFrontBufferOnWhite);
-    if (mFrontBuffer->GetIPDLActor() &&
-        mCompositableClient && mCompositableClient->GetIPDLActor()) {
-      // remove old buffer from CompositableHost
-      RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
-      RefPtr<AsyncTransactionTracker> tracker =
-          new RemoveTextureFromCompositableTracker(waiter);
-      // Hold TextureClient until transaction complete.
-      tracker->SetTextureClient(mFrontBuffer);
-      mFrontBuffer->SetRemoveFromCompositableWaiter(waiter);
-      // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
-      mManager->AsShadowForwarder()->RemoveTextureFromCompositableAsync(tracker,
-                                                                        mCompositableClient,
-                                                                        mFrontBuffer);
+
+    if (mCompositableClient) {
+      mFrontBuffer->RemoveFromCompositable(mCompositableClient);
     }
-#endif
+
     mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
     if (mFrontBufferOnWhite) {
+      mFrontBufferOnWhite->RemoveFromCompositable(mCompositableClient);
       mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
     }
     mFrontLock->ReadUnlock();
     if (mFrontBuffer->IsLocked()) {
       mFrontBuffer->Unlock();
     }
     if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
       mFrontBufferOnWhite->Unlock();
@@ -738,17 +720,17 @@ TileClient::GetBackBuffer(const nsIntReg
                           gfxContentType aContent,
                           SurfaceMode aMode,
                           nsIntRegion& aAddPaintedRegion,
                           RefPtr<TextureClient>* aBackBufferOnWhite)
 {
   // Try to re-use the front-buffer if possible
   bool createdTextureClient = false;
   if (mFrontBuffer &&
-      mFrontBuffer->HasInternalBuffer() &&
+      mFrontBuffer->HasIntermediateBuffer() &&
       mFrontLock->GetReadCount() == 1 &&
       !(aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mFrontBufferOnWhite)) {
     // If we had a backbuffer we no longer care about it since we'll
     // re-use the front buffer.
     DiscardBackBuffer();
     Flip();
   } else {
     if (!mBackBuffer ||
@@ -1375,17 +1357,17 @@ ClientMultiTiledLayerBuffer::ValidateTil
 
   backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution);
 
   aTile.Flip();
 
   // Note, we don't call UpdatedTexture. The Updated function is called manually
   // by the TiledContentHost before composition.
 
-  if (backBuffer->HasInternalBuffer()) {
+  if (backBuffer->HasIntermediateBuffer()) {
     // If our new buffer has an internal buffer, we don't want to keep another
     // TextureClient around unnecessarily, so discard the back-buffer.
     aTile.DiscardBackBuffer();
   }
 
   return true;
 }
 
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -233,16 +233,21 @@ public:
   virtual void Unlock() { }
 
   virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) {
     return nullptr;
   }
 
   virtual int32_t GetLastInputFrameID() const { return -1; }
 
+  /// Called when shutting down the layer tree.
+  /// This is a good place to clear all potential gpu resources before the widget
+  /// is is destroyed.
+  virtual void CleanupResources() {}
+
 protected:
   TextureInfo mTextureInfo;
   uint64_t mAsyncID;
   uint64_t mCompositorID;
   RefPtr<Compositor> mCompositor;
   Layer* mLayer;
   uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
   bool mAttached;
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -47,65 +47,46 @@ ImageHost::UseTextureHost(const nsTArray
 {
   MOZ_ASSERT(!mLocked);
 
   CompositableHost::UseTextureHost(aTextures);
   MOZ_ASSERT(aTextures.Length() >= 1);
 
   nsTArray<TimedImage> newImages;
 
-  // Remove all mImages without an mTextureSource to recycle.
-  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
-    if (!mImages[i].mTextureSource) {
-      mImages.RemoveElementAt(i);
-    }
-  }
-
-  // Create new TimedImage entries and recycle any mTextureSources that match
-  // our mFrontBuffers.
   for (uint32_t i = 0; i < aTextures.Length(); ++i) {
     const TimedTexture& t = aTextures[i];
     MOZ_ASSERT(t.mTexture);
     if (i + 1 < aTextures.Length() &&
         t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
       // Ignore frames before a frame that we already composited. We don't
       // ever want to display these frames. This could be important if
       // the frame producer adjusts timestamps (e.g. to track the audio clock)
       // and the new frame times are earlier.
       continue;
     }
     TimedImage& img = *newImages.AppendElement();
-    img.mFrontBuffer = t.mTexture;
-    for (uint32_t i = 0; i < mImages.Length(); ++i) {
-      if (mImages[i].mFrontBuffer == img.mFrontBuffer) {
-        img.mTextureSource = mImages[i].mTextureSource;
-        mImages.RemoveElementAt(i);
-        break;
-      }
-    }
+    img.mTextureHost = t.mTexture;
     img.mTimeStamp = t.mTimeStamp;
     img.mPictureRect = t.mPictureRect;
     img.mFrameID = t.mFrameID;
     img.mProducerID = t.mProducerID;
     img.mInputFrameID = t.mInputFrameID;
+    img.mTextureHost->SetCropRect(img.mPictureRect);
+    img.mTextureHost->Updated();
   }
-  // Recycle any leftover mTextureSources and call PrepareTextureSource on all
-  // images.
-  for (auto& img : newImages) {
-    if (!img.mTextureSource && !mImages.IsEmpty()) {
-      img.mTextureSource = mImages.LastElement().mTextureSource;
-      mImages.RemoveElementAt(mImages.Length() - 1);
-    }
-    // SetCropRect() affects only on a specific platform.
-    // If it is not implemented, it does nothing.
-    img.mFrontBuffer->SetCropRect(img.mPictureRect);
-    img.mFrontBuffer->Updated();
-    img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
+
+  mImages.SwapElements(newImages);
+  newImages.Clear();
+
+  // If we only have one image we can upload it right away, otherwise we'll upload
+  // on-demand during composition after we have picked the proper timestamp.
+  if (mImages.Length() == 1) {
+    SetCurrentTextureHost(mImages[0].mTextureHost);
   }
-  mImages.SwapElements(newImages);
 
   // Video producers generally send replacement images with the same frameID but
   // slightly different timestamps in order to sync with the audio clock. This
   // means that any CompositeUntil() call we made in Composite() may no longer
   // guarantee that we'll composite until the next frame is ready. Fix that here.
   if (GetCompositor() && mLastFrameID >= 0) {
     for (size_t i = 0; i < mImages.Length(); ++i) {
       bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
@@ -115,24 +96,66 @@ ImageHost::UseTextureHost(const nsTArray
                                         TimeDuration::FromMilliseconds(BIAS_TIME_MS));
         break;
       }
     }
   }
 }
 
 void
+ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
+{
+  if (aTexture == mCurrentTextureHost.get()) {
+    return;
+  }
+
+  bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
+                            && mCurrentTextureHost->HasIntermediateBuffer();
+
+  if (swapTextureSources) {
+    auto dataSource = mCurrentTextureSource->AsDataTextureSource();
+    if (dataSource) {
+      // The current textureHost has an internal buffer in the form of the
+      // DataTextureSource. Removing the ownership of the texture source
+      // will enable the next texture host we bind to the texture source to
+      // acquire it instead of creating a new one. This is desirable in
+      // ImageHost because the current texture won't be used again with the
+      // same content. It wouldn't be desirable with ContentHost for instance,
+      // because the latter reuses the texture's valid regions.
+      dataSource->SetOwner(nullptr);
+    }
+
+    RefPtr<TextureSource> tmp = mExtraTextureSource;
+    mExtraTextureSource = mCurrentTextureSource.get();
+    mCurrentTextureSource = tmp;
+  } else {
+    mExtraTextureSource = nullptr;
+  }
+
+  mCurrentTextureHost = aTexture;
+  mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
+}
+
+void
+ImageHost::CleanupResources()
+{
+  mExtraTextureSource = nullptr;
+  mCurrentTextureSource = nullptr;
+  mCurrentTextureHost = nullptr;
+}
+
+void
 ImageHost::RemoveTextureHost(TextureHost* aTexture)
 {
   MOZ_ASSERT(!mLocked);
 
   CompositableHost::RemoveTextureHost(aTexture);
 
   for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
-    if (mImages[i].mFrontBuffer == aTexture) {
+    if (mImages[i].mTextureHost == aTexture) {
       aTexture->UnbindTextureSource();
       mImages.RemoveElementAt(i);
     }
   }
 }
 
 void
 ImageHost::UseOverlaySource(OverlaySource aOverlay,
@@ -240,33 +263,35 @@ ImageHost::TimedImage* ImageHost::Choose
   int index = ChooseImageIndex();
   return index >= 0 ? &mImages[index] : nullptr;
 }
 
 TextureHost*
 ImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
   TimedImage* img = ChooseImage();
+  if (img) {
+    SetCurrentTextureHost(img->mTextureHost);
+  }
   if (aPictureRect && img) {
     *aPictureRect = img->mPictureRect;
   }
-  return img ? img->mFrontBuffer.get() : nullptr;
+  return img ? img->mTextureHost.get() : nullptr;
 }
 
 void ImageHost::Attach(Layer* aLayer,
                        Compositor* aCompositor,
                        AttachFlags aFlags)
 {
   CompositableHost::Attach(aLayer, aCompositor, aFlags);
   for (auto& img : mImages) {
     if (GetCompositor()) {
-      img.mFrontBuffer->SetCompositor(GetCompositor());
+      img.mTextureHost->SetCompositor(GetCompositor());
     }
-    img.mFrontBuffer->Updated();
-    img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
+    img.mTextureHost->Updated();
   }
 }
 
 void
 ImageHost::Composite(LayerComposite* aLayer,
                      EffectChain& aEffectChain,
                      float aOpacity,
                      const gfx::Matrix4x4& aTransform,
@@ -300,44 +325,45 @@ ImageHost::Composite(LayerComposite* aLa
     return;
   }
 
   if (uint32_t(imageIndex) + 1 < mImages.Length()) {
     GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
   }
 
   TimedImage* img = &mImages[imageIndex];
+  SetCurrentTextureHost(img->mTextureHost);
   // Make sure the front buffer has a compositor
-  img->mFrontBuffer->SetCompositor(GetCompositor());
-  if (img->mTextureSource) {
-    img->mTextureSource->SetCompositor(GetCompositor());
+  mCurrentTextureHost->SetCompositor(GetCompositor());
+  if (mCurrentTextureSource) {
+    mCurrentTextureSource->SetCompositor(GetCompositor());
   }
 
   {
     AutoLockCompositableHost autoLock(this);
     if (autoLock.Failed()) {
       NS_WARNING("failed to lock front buffer");
       return;
     }
 
-    if (!img->mFrontBuffer->BindTextureSource(img->mTextureSource)) {
+    if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
       return;
     }
 
-    if (!img->mTextureSource) {
+    if (!mCurrentTextureSource) {
       // BindTextureSource above should have returned false!
       MOZ_ASSERT(false);
       return;
     }
 
     bool isAlphaPremultiplied =
-        !(img->mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+        !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
     RefPtr<TexturedEffect> effect =
-        CreateTexturedEffect(img->mFrontBuffer->GetReadFormat(),
-            img->mTextureSource.get(), aFilter, isAlphaPremultiplied,
+        CreateTexturedEffect(mCurrentTextureHost->GetFormat(),
+            mCurrentTextureSource.get(), aFilter, isAlphaPremultiplied,
             GetRenderState());
     if (!effect) {
       return;
     }
 
     if (!GetCompositor()->SupportsEffect(effect->mType)) {
       return;
     }
@@ -358,65 +384,65 @@ ImageHost::Composite(LayerComposite* aLa
                 img->mFrameID, img->mProducerID));
       }
       mLastFrameID = img->mFrameID;
       mLastProducerID = img->mProducerID;
       mLastInputFrameID = img->mInputFrameID;
     }
     aEffectChain.mPrimaryEffect = effect;
     gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
-    BigImageIterator* it = img->mTextureSource->AsBigImageIterator();
+    BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
     if (it) {
 
       // This iteration does not work if we have multiple texture sources here
       // (e.g. 3 YCbCr textures). There's nothing preventing the different
       // planes from having different resolutions or tile sizes. For example, a
       // YCbCr frame could have Cb and Cr planes that are half the resolution of
       // the Y plane, in such a way that the Y plane overflows the maximum
       // texture size and the Cb and Cr planes do not. Then the Y plane would be
       // split into multiple tiles and the Cb and Cr planes would just be one
       // tile each.
       // To handle the general case correctly, we'd have to create a grid of
       // intersected tiles over all planes, and then draw each grid tile using
       // the corresponding source tiles from all planes, with appropriate
       // per-plane per-tile texture coords.
       // DrawQuad currently assumes that all planes use the same texture coords.
-      MOZ_ASSERT(it->GetTileCount() == 1 || !img->mTextureSource->GetNextSibling(),
+      MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
                  "Can't handle multi-plane BigImages");
 
       it->BeginBigImageIteration();
       do {
         IntRect tileRect = it->GetTileRect();
         gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
         rect = rect.Intersect(pictureRect);
         effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
                                       Float(rect.y - tileRect.y) / tileRect.height,
                                       Float(rect.width) / tileRect.width,
                                       Float(rect.height) / tileRect.height);
-        if (img->mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+        if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
           effect->mTextureCoords.y = effect->mTextureCoords.YMost();
           effect->mTextureCoords.height = -effect->mTextureCoords.height;
         }
         GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
                                   aOpacity, aTransform);
         GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
                                          rect, aClipRect, aTransform, mFlashCounter);
       } while (it->NextTile());
       it->EndBigImageIteration();
       // layer border
       GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
                                        aClipRect, aTransform, mFlashCounter);
     } else {
-      IntSize textureSize = img->mTextureSource->GetSize();
+      IntSize textureSize = mCurrentTextureSource->GetSize();
       effect->mTextureCoords = Rect(Float(img->mPictureRect.x) / textureSize.width,
                                     Float(img->mPictureRect.y) / textureSize.height,
                                     Float(img->mPictureRect.width) / textureSize.width,
                                     Float(img->mPictureRect.height) / textureSize.height);
 
-      if (img->mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+      if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
         effect->mTextureCoords.y = effect->mTextureCoords.YMost();
         effect->mTextureCoords.height = -effect->mTextureCoords.height;
       }
 
       GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
                                 aOpacity, aTransform);
       GetCompositor()->DrawDiagnostics(diagnosticFlags,
                                        pictureRect, aClipRect,
@@ -436,17 +462,17 @@ ImageHost::Composite(LayerComposite* aLa
       mBias);
 }
 
 void
 ImageHost::SetCompositor(Compositor* aCompositor)
 {
   if (mCompositor != aCompositor) {
     for (auto& img : mImages) {
-      img.mFrontBuffer->SetCompositor(aCompositor);
+      img.mTextureHost->SetCompositor(aCompositor);
     }
   }
   if (mImageHostOverlay) {
     mImageHostOverlay->SetCompositor(aCompositor);
   }
   CompositableHost::SetCompositor(aCompositor);
 }
 
@@ -455,17 +481,17 @@ ImageHost::PrintInfo(std::stringstream& 
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
 
   nsAutoCString pfx(aPrefix);
   pfx += "  ";
   for (auto& img : mImages) {
     aStream << "\n";
-    img.mFrontBuffer->PrintInfo(aStream, pfx.get());
+    img.mTextureHost->PrintInfo(aStream, pfx.get());
     AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
   }
 
   if (mImageHostOverlay) {
     mImageHostOverlay->PrintInfo(aStream, aPrefix);
   }
 }
 
@@ -473,71 +499,75 @@ void
 ImageHost::Dump(std::stringstream& aStream,
                 const char* aPrefix,
                 bool aDumpHtml)
 {
   for (auto& img : mImages) {
     aStream << aPrefix;
     aStream << (aDumpHtml ? "<ul><li>TextureHost: "
                              : "TextureHost: ");
-    DumpTextureHost(aStream, img.mFrontBuffer);
+    DumpTextureHost(aStream, img.mTextureHost);
     aStream << (aDumpHtml ? " </li></ul> " : " ");
   }
 }
 
 LayerRenderState
 ImageHost::GetRenderState()
 {
   if (mImageHostOverlay) {
     return mImageHostOverlay->GetRenderState();
   }
 
   TimedImage* img = ChooseImage();
   if (img) {
-    return img->mFrontBuffer->GetRenderState();
+    SetCurrentTextureHost(img->mTextureHost);
+    return img->mTextureHost->GetRenderState();
   }
   return LayerRenderState();
 }
 
 already_AddRefed<gfx::DataSourceSurface>
 ImageHost::GetAsSurface()
 {
   if (mImageHostOverlay) {
     return nullptr;
   }
 
   TimedImage* img = ChooseImage();
   if (img) {
-    return img->mFrontBuffer->GetAsSurface();
+    return img->mTextureHost->GetAsSurface();
   }
   return nullptr;
 }
 
 bool
 ImageHost::Lock()
 {
   MOZ_ASSERT(!mLocked);
   TimedImage* img = ChooseImage();
   if (!img) {
     return false;
   }
-  if (!img->mFrontBuffer->Lock()) {
+
+  SetCurrentTextureHost(img->mTextureHost);
+
+  if (!mCurrentTextureHost->Lock()) {
     return false;
   }
   mLocked = true;
   return true;
 }
 
 void
 ImageHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
-  TimedImage* img = ChooseImage();
-  if (img) {
-    img->mFrontBuffer->Unlock();
+
+  if (mCurrentTextureHost) {
+    mCurrentTextureHost->Unlock();
   }
   mLocked = false;
 }
 
 IntSize
 ImageHost::GetImageSize() const
 {
   if (mImageHostOverlay) {
@@ -553,26 +583,27 @@ ImageHost::GetImageSize() const
 
 already_AddRefed<TexturedEffect>
 ImageHost::GenEffect(const gfx::Filter& aFilter)
 {
   TimedImage* img = ChooseImage();
   if (!img) {
     return nullptr;
   }
-  if (!img->mFrontBuffer->BindTextureSource(img->mTextureSource)) {
+  SetCurrentTextureHost(img->mTextureHost);
+  if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
     return nullptr;
   }
   bool isAlphaPremultiplied = true;
-  if (img->mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
+  if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
     isAlphaPremultiplied = false;
   }
 
-  return CreateTexturedEffect(img->mFrontBuffer->GetReadFormat(),
-                              img->mTextureSource,
+  return CreateTexturedEffect(mCurrentTextureHost->GetFormat(),
+                              mCurrentTextureSource,
                               aFilter,
                               isAlphaPremultiplied,
                               GetRenderState());
 }
 
 void
 ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
 {
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -81,16 +81,20 @@ public:
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) override;
 
+  void SetCurrentTextureHost(TextureHost* aTexture);
+
+  virtual void CleanupResources() override;
+
   int32_t GetFrameID()
   {
     const TimedImage* img = ChooseImage();
     return img ? img->mFrameID : -1;
   }
 
   int32_t GetProducerID()
   {
@@ -108,25 +112,31 @@ public:
     // Apply a negative bias to frame times to keep them before the vsync time
     BIAS_NEGATIVE,
     // Apply a positive bias to frame times to keep them after the vsync time
     BIAS_POSITIVE,
   };
 
 protected:
   struct TimedImage {
-    CompositableTextureHostRef mFrontBuffer;
-    CompositableTextureSourceRef mTextureSource;
+    RefPtr<TextureHost> mTextureHost;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
     int32_t mFrameID;
     int32_t mProducerID;
     int32_t mInputFrameID;
   };
 
+  CompositableTextureHostRef mCurrentTextureHost;
+  CompositableTextureSourceRef mCurrentTextureSource;
+  // When doing texture uploads it's best to alternate between two (or three)
+  // texture sources so that the texture we upload to isn't being used by
+  // the GPU to composite the previous frame.
+  RefPtr<TextureSource> mExtraTextureSource;
+
   /**
    * ChooseImage is guaranteed to return the same TimedImage every time it's
    * called during the same composition, up to the end of Composite() ---
    * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
    * mBias is updated at the end of Composite().
    */
   const TimedImage* ChooseImage() const;
   TimedImage* ChooseImage();
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -152,16 +152,17 @@ ImageLayerComposite::GetCompositableHost
 
   return nullptr;
 }
 
 void
 ImageLayerComposite::CleanupResources()
 {
   if (mImageHost) {
+    mImageHost->CleanupResources();
     mImageHost->Detach(this);
   }
   mImageHost = nullptr;
 }
 
 gfx::Filter
 ImageLayerComposite::GetEffectFilter()
 {
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -390,22 +390,24 @@ BufferTextureHost::BufferTextureHost(con
 , mNeedsFullUpdate(false)
 {
   mDescriptor = aDesc;
   switch (mDescriptor.type()) {
     case BufferDescriptor::TYCbCrDescriptor: {
       const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
       mSize = ycbcr.ySize();
       mFormat = gfx::SurfaceFormat::YUV;
+      mHasIntermediateBuffer = true;
       break;
     }
     case BufferDescriptor::TRGBDescriptor: {
       const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
       mSize = rgb.size();
       mFormat = rgb.format();
+      mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
       break;
     }
     default: MOZ_CRASH();
   }
   if (aFlags & TextureFlags::COMPONENT_ALPHA) {
     // One texture of a component alpha texture pair will start out all white.
     // This hack allows us to easily make sure that white will be uploaded.
     // See bug 1138934
@@ -440,23 +442,37 @@ BufferTextureHost::SetCompositor(Composi
   if (mCompositor == aCompositor) {
     return;
   }
   RefPtr<TextureSource> it = mFirstSource;
   while (it) {
     it->SetCompositor(aCompositor);
     it = it->GetNextSibling();
   }
+  if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+    mFirstSource->SetOwner(nullptr);
+  }
   mFirstSource = nullptr;
   mCompositor = aCompositor;
 }
 
 void
 BufferTextureHost::DeallocateDeviceData()
 {
+  if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+    return;
+  }
+
+  if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+    mFirstSource = nullptr;
+    return;
+  }
+
+  mFirstSource->SetOwner(nullptr);
+
   RefPtr<TextureSource> it = mFirstSource;
   while (it) {
     it->DeallocateDeviceData();
     it = it->GetNextSibling();
   }
 }
 
 bool
@@ -473,16 +489,83 @@ BufferTextureHost::Lock()
 void
 BufferTextureHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
   mLocked = false;
 }
 
 bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+  MOZ_ASSERT(!mHasIntermediateBuffer);
+  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV);
+
+  if (mFirstSource) {
+    return true;
+  }
+
+  if (!mCompositor) {
+    return false;
+  }
+
+  RefPtr<gfx::DataSourceSurface> surf =
+    gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+      ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+
+  if (!surf) {
+    return false;
+  }
+
+  mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+  mFirstSource->SetUpdateSerial(mUpdateSerial);
+  mFirstSource->SetOwner(this);
+
+  return true;
+}
+
+void
+BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
+{
+  if (!mHasIntermediateBuffer) {
+    EnsureWrappingTextureSource();
+  }
+
+  if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+    // We are already attached to a TextureSource, nothing to do except tell
+    // the compositable to use it.
+    aTexture = mFirstSource.get();
+    return;
+  }
+
+  // We don't own it, apparently.
+  mFirstSource = nullptr;
+
+  DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+  bool compatibleFormats = texture
+                         && (mFormat == texture->GetFormat()
+                             || (mFormat == gfx::SurfaceFormat::YUV
+                                 && mCompositor->SupportsEffect(EffectTypes::YCBCR)
+                                 && texture->GetNextSibling())
+                             || (mFormat == gfx::SurfaceFormat::YUV
+                                 && !mCompositor->SupportsEffect(EffectTypes::YCBCR)
+                                 && texture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8));
+
+  bool shouldCreateTexture = !compatibleFormats
+                           || texture->NumCompositableRefs() > 1
+                           || texture->HasOwner()
+                           || texture->GetSize() != mSize;
+
+  if (!shouldCreateTexture) {
+    mFirstSource = texture;
+    mFirstSource->SetOwner(this);
+  }
+}
+
+bool
 BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
 {
   MOZ_ASSERT(mLocked);
   MOZ_ASSERT(mFirstSource);
   aTexture = mFirstSource;
   return !!aTexture;
 }
 
@@ -500,19 +583,27 @@ BufferTextureHost::GetFormat() const
     return gfx::SurfaceFormat::R8G8B8X8;
   }
   return mFormat;
 }
 
 bool
 BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
 {
-  if (mFirstSource && mFirstSource->GetUpdateSerial() == mUpdateSerial) {
+  auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+  if (serial == mUpdateSerial) {
     return true;
   }
+
+  if (serial == 0) {
+    // 0 means the source has no valid content
+    aRegion = nullptr;
+  }
+
   if (!Upload(aRegion)) {
     return false;
   }
 
   // We no longer have an invalid region.
   mNeedsFullUpdate = false;
   mMaybeUpdatedRegion.SetEmpty();
 
@@ -533,44 +624,50 @@ BufferTextureHost::Upload(nsIntRegion *a
     // deserializing it.
     return false;
   }
   if (!mCompositor) {
     // This can happen if we send textures to a compositable that isn't yet
     // attached to a layer.
     return false;
   }
+  if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+    return true;
+  }
+
   if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
     NS_WARNING("BufferTextureHost: unsupported format!");
     return false;
   } else if (mFormat == gfx::SurfaceFormat::YUV) {
     const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
 
     if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
       RefPtr<gfx::DataSourceSurface> surf =
         ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
       if (NS_WARN_IF(!surf)) {
         return false;
       }
       if (!mFirstSource) {
         mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+        mFirstSource->SetOwner(this);
       }
       mFirstSource->Update(surf, aRegion);
       return true;
     }
 
     RefPtr<DataTextureSource> srcY;
     RefPtr<DataTextureSource> srcU;
     RefPtr<DataTextureSource> srcV;
     if (!mFirstSource) {
       // We don't support BigImages for YCbCr compositing.
       srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
       srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
       srcV = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
       mFirstSource = srcY;
+      mFirstSource->SetOwner(this);
       srcY->SetNextSibling(srcU);
       srcU->SetNextSibling(srcV);
     } else {
       // mFormat never changes so if this was created as a YCbCr host and already
       // contains a source it should already have 3 sources.
       // BufferTextureHost only uses DataTextureSources so it is safe to assume
       // all 3 sources are DataTextureSource.
       MOZ_ASSERT(mFirstSource->GetNextSibling());
@@ -606,16 +703,17 @@ BufferTextureHost::Upload(nsIntRegion *a
       NS_WARNING("failed to update the DataTextureSource");
       return false;
     }
   } else {
     // non-YCbCr case
     nsIntRegion* regionToUpdate = aRegion;
     if (!mFirstSource) {
       mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+      mFirstSource->SetOwner(this);
       if (mFlags & TextureFlags::COMPONENT_ALPHA) {
         // Update the full region the first time for component alpha textures.
         regionToUpdate = nullptr;
       }
     }
 
     RefPtr<gfx::DataSourceSurface> surf =
       gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -227,17 +227,18 @@ typedef CompositableTextureRef<TextureHo
  * Interface for TextureSources that can be updated from a DataSourceSurface.
  *
  * All backend should implement at least one DataTextureSource.
  */
 class DataTextureSource : public TextureSource
 {
 public:
   DataTextureSource()
-    : mUpdateSerial(0)
+    : mOwner(0)
+    , mUpdateSerial(0)
   {}
 
   virtual const char* Name() const override { return "DataTextureSource"; }
 
   virtual DataTextureSource* AsDataTextureSource() override { return this; }
 
   /**
    * Upload a (portion of) surface to the TextureSource.
@@ -272,17 +273,33 @@ public:
    * Provide read access to the data as a DataSourceSurface.
    *
    * This is expected to be very slow and should be used for mostly debugging.
    * XXX - implement everywhere and make it pure virtual.
    */
   virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() { return nullptr; };
 #endif
 
+  void SetOwner(TextureHost* aOwner)
+  {
+    auto newOwner = (uintptr_t)aOwner;
+    if (newOwner != mOwner) {
+      mOwner = newOwner;
+      SetUpdateSerial(0);
+    }
+  }
+
+  bool IsOwnedBy(TextureHost* aOwner) const { return mOwner == (uintptr_t)aOwner; }
+
+  bool HasOwner() const { return !IsOwnedBy(nullptr); }
+
 private:
+  // We store mOwner as an integer rather than as a pointer to make it clear
+  // it is not intended to be dereferenced.
+  uintptr_t mOwner;
   uint32_t mUpdateSerial;
 };
 
 /**
  * TextureHost is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * compositor-side half of a TextureClient/TextureHost pair. A corresponding
  * TextureClient lives on the content-side.
@@ -508,17 +525,17 @@ public:
   virtual const char *Name() { return "TextureHost"; }
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   /**
    * Indicates whether the TextureHost implementation is backed by an
    * in-memory buffer. The consequence of this is that locking the
    * TextureHost does not contend with locking the texture on the client side.
    */
-  virtual bool HasInternalBuffer() const { return false; }
+  virtual bool HasIntermediateBuffer() const { return false; }
 
   void AddCompositableRef() { ++mCompositableCount; }
 
   void ReleaseCompositableRef()
   {
     --mCompositableCount;
     MOZ_ASSERT(mCompositableCount >= 0);
     if (mCompositableCount == 0) {
@@ -587,16 +604,18 @@ public:
   virtual uint8_t* GetBuffer() = 0;
 
   virtual size_t GetBufferSize() = 0;
 
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
+  virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
   virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
 
   virtual void DeallocateDeviceData() override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
   /**
    * Return the format that is exposed to the compositor when calling
@@ -606,33 +625,35 @@ public:
    * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
    */
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
 
 protected:
   bool Upload(nsIntRegion *aRegion = nullptr);
   bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+  bool EnsureWrappingTextureSource();
 
   virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
 
   BufferDescriptor mDescriptor;
   RefPtr<Compositor> mCompositor;
   RefPtr<DataTextureSource> mFirstSource;
   nsIntRegion mMaybeUpdatedRegion;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   uint32_t mUpdateSerial;
   bool mLocked;
   bool mNeedsFullUpdate;
+  bool mHasIntermediateBuffer;
 };
 
 /**
  * TextureHost that wraps shared memory.
  * the corresponding texture on the client side is ShmemTextureClient.
  * This TextureHost is backend-independent.
  */
 class ShmemTextureHost : public BufferTextureHost
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -144,17 +144,17 @@ UseTileTexture(CompositableTextureHostRe
 
   if (!aUpdateRect.IsEmpty()) {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     aTexture->Updated(nullptr);
 #else
     // We possibly upload the entire texture contents here. This is a purposeful
     // decision, as sub-image upload can often be slow and/or unreliable, but
     // we may want to reevaluate this in the future.
-    // For !HasInternalBuffer() textures, this is likely a no-op.
+    // For !HasIntermediateBuffer() textures, this is likely a no-op.
     nsIntRegion region = aUpdateRect;
     aTexture->Updated(&region);
 #endif
   }
 
   aTexture->PrepareTextureSource(aTextureSource);
 }
 
@@ -362,17 +362,17 @@ TiledLayerBufferComposite::UseTiles(cons
 
     if (tile.mTextureHostOnWhite) {
       UseTileTexture(tile.mTextureHostOnWhite,
                      tile.mTextureSourceOnWhite,
                      texturedDesc.updateRect(),
                      aCompositor);
     }
 
-    if (tile.mTextureHost->HasInternalBuffer()) {
+    if (tile.mTextureHost->HasIntermediateBuffer()) {
       // Now that we did the texture upload (in UseTileTexture), we can release
       // the lock.
       tile.ReadUnlock();
     }
   }
 
   mTiles = newTiles;
   mTileSize = aTiles.tileSize();
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -25,17 +25,17 @@ class DXGITextureData : public TextureDa
 {
 public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
  
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
  
   virtual bool SupportsMoz2D() const override { return true; }
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual bool HasSynchronization() const override { return mHasSynchronization; }
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
 
   static DXGITextureData*
   Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
 
@@ -137,17 +137,17 @@ public:
 
   virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
 
   // TODO - DXGIYCbCrTextureClient returned true but that looks like a mistake
-  virtual bool HasInternalBuffer() const override{ return false; }
+  virtual bool HasIntermediateBuffer() const override{ return false; }
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return gfx::SurfaceFormat::YUV; }
 
   virtual bool SupportsMoz2D() const override { return false; }
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override { return nullptr; }
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -179,17 +179,17 @@ protected:
  */
 class D3D9TextureData : public TextureData
 {
 public:
   ~D3D9TextureData();
 
   virtual bool SupportsMoz2D() const override { return true; }
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasIntermediateBuffer() const override { return true; }
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
 
   virtual bool Lock(OpenMode aMode, FenceHandle*) override;
 
   virtual void Unlock() override;
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
@@ -241,17 +241,17 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual void Deallocate(ISurfaceAllocator* aAllocator) override {}
 
   IDirect3DDevice9* GetD3D9Device() { return mDevice; }
   IDirect3DTexture9* GetD3D9Texture() { return mTexture; }
   HANDLE GetShareHandle() const { return mHandle; }
   already_AddRefed<IDirect3DSurface9> GetD3D9Surface() const;
 
@@ -292,17 +292,17 @@ public:
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
   {
     return nullptr;
   }
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasIntermediateBuffer() const override { return true; }
 
 protected:
   TextureHostD3D9(TextureFlags aFlags);
   IDirect3DDevice9* GetDevice();
 
   virtual void UpdatedInternal(const nsIntRegion* aRegion) override;
 
   RefPtr<DataTextureSourceD3D9> mTextureSource;
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -167,16 +167,18 @@ public:
   {
     return mTextureFactoryIdentifier;
   }
 
   int32_t GetSerial() { return mSerial; }
 
   SyncObject* GetSyncObject() { return mSyncObject; }
 
+  virtual CompositableForwarder* AsCompositableForwarder() override { return this; }
+
 protected:
   TextureFactoryIdentifier mTextureFactoryIdentifier;
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
   RefPtr<SyncObject> mSyncObject;
   const int32_t mSerial;
   static mozilla::Atomic<int32_t> sSerialCounter;
 };
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -142,17 +142,21 @@ ISurfaceAllocator::AllocSurfaceDescripto
     mozilla::ipc::Shmem shmem;
     if (!AllocUnsafeShmem(size, shmemType, &shmem)) {
       return false;
     }
 
     bufferDesc = shmem;
   }
 
-  *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format), bufferDesc);
+  // Use an intermediate buffer by default. Skipping the intermediate buffer is
+  // only possible in certain configurations so let's keep it simple here for now.
+  const bool hasIntermediateBuffer = true;
+  *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+                                     bufferDesc);
 
   return true;
 }
 
 /* static */ bool
 ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface)
 {
   return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -36,16 +36,17 @@ class Shmem;
 } // namespace ipc
 namespace gfx {
 class DataSourceSurface;
 } // namespace gfx
 
 namespace layers {
 
 class MaybeMagicGrallocBufferHandle;
+class CompositableForwarder;
 
 enum BufferCapabilities {
   DEFAULT_BUFFER_CAPS = 0,
   /**
    * The allocated buffer must be efficiently mappable as a DataSourceSurface.
    */
   MAP_AS_IMAGE_SURFACE = 1 << 0,
   /**
@@ -155,16 +156,17 @@ public:
   virtual MessageLoop * GetMessageLoop() const
   {
     return mDefaultMessageLoop;
   }
 
   // Returns true if aSurface wraps a Shmem.
   static bool IsShmem(SurfaceDescriptor* aSurface);
 
+  virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
 protected:
 
   virtual bool IsOnCompositorSide() const = 0;
 
   virtual ~ISurfaceAllocator();
 
   void ShrinkShmemSectionHeap();
 
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -96,16 +96,17 @@ struct SurfaceDescriptorSharedGLTexture 
 struct SurfaceDescriptorGralloc {
   MaybeMagicGrallocBufferHandle buffer;
   bool isOpaque;
 };
 
 struct RGBDescriptor {
   IntSize size;
   SurfaceFormat format;
+  bool hasIntermediateBuffer;
 };
 
 struct YCbCrDescriptor {
   IntSize ySize;
   IntSize cbCrSize;
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -410,17 +410,17 @@ ShadowLayerForwarder::UseTextures(Compos
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
     FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle();
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()),
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID, t.mInputFrameID));
     if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
-        && t.mTextureClient->HasInternalBuffer()) {
+        && t.mTextureClient->HasIntermediateBuffer()) {
 
       // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
       // race with updates on the main thread. In this case we want the transaction
       // to be synchronous.
       mTxn->MarkSyncTransaction();
     }
   }
   mTxn->AddEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -53,17 +53,17 @@ public:
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
 
   virtual bool CanExposeMappedData() const override { return true; }
 
   virtual bool BorrowMappedData(MappedTextureData& aMap) override;
 
   virtual bool SupportsMoz2D() const override { return true; }
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual bool HasSynchronization() const override { return true; }
 
   virtual void Deallocate(ISurfaceAllocator*) override;
 
   virtual void Forget(ISurfaceAllocator*) override;
 
   static GrallocTextureData* CreateForDrawing(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
@@ -104,16 +104,18 @@ public:
   android::sp<android::GraphicBuffer> GetGraphicBuffer() { return mGraphicBuffer; }
 
   virtual void WaitForFence(FenceHandle* aFence) override;
 
   ~GrallocTextureData();
 
   virtual TextureFlags GetTextureFlags() const override;
 
+  virtual GrallocTextureData* AsGrallocTextureData() { return this; }
+
 protected:
   GrallocTextureData(MaybeMagicGrallocBufferHandle aGrallocHandle,
                      gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                      gfx::BackendType aMoz2DBackend);
 
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   gfx::BackendType mMoz2DBackend;
--- a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -25,17 +25,17 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual void Deallocate(ISurfaceAllocator* aAllocator) override { mSurface = nullptr; }
 
   virtual void Forget(ISurfaceAllocator* aAllocator) override { mSurface = nullptr; }
 
   // For debugging purposes only.
   already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
 
--- a/gfx/layers/opengl/TextureClientOGL.h
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -23,17 +23,17 @@ namespace layers {
 class EGLImageTextureData : public TextureData
 {
 public:
 
   static already_AddRefed<TextureClient>
   CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
                       ISurfaceAllocator* aAllocator, TextureFlags aFlags);
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
 
   virtual void Deallocate(ISurfaceAllocator*) override { mImage = nullptr; }
 
   virtual void Forget(ISurfaceAllocator*) override { mImage = nullptr; }
@@ -64,17 +64,17 @@ public:
   CreateTextureClient(gl::AndroidSurfaceTexture* aSurfTex,
                       gfx::IntSize aSize,
                       gl::OriginPos aOriginPos,
                       ISurfaceAllocator* aAllocator,
                       TextureFlags aFlags);
 
   ~AndroidSurfaceTextureData();
 
-  virtual bool HasInternalBuffer() const override { return false; }
+  virtual bool HasIntermediateBuffer() const override { return false; }
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
 
   // Useless functions.
   virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -128,19 +128,22 @@ void
 nsPNGDecoder::CheckForTransparency(SurfaceFormat aFormat,
                                    const IntRect& aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
   if (aFormat == SurfaceFormat::B8G8R8A8) {
     PostHasTransparency();
   }
 
-  // PNGs shouldn't have first-frame padding.
-  MOZ_ASSERT_IF(mNumFrames == 0,
-                IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect));
+  // If the first frame of animated image doesn't draw into the whole image,
+  // then record that it is transparent.
+  if (mNumFrames == 0 && !IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect)) {
+    MOZ_ASSERT(HasAnimation());
+    PostHasTransparency();
+  }
 }
 
 // CreateFrame() is used for both simple and animated images
 nsresult
 nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset,
                           int32_t aWidth, int32_t aHeight,
                           gfx::SurfaceFormat aFormat)
 {
@@ -592,16 +595,22 @@ nsPNGDecoder::info_callback(png_structp 
       MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
                              "for an animated image?");
       decoder->mDownscaler.reset();
     }
   }
 #endif
 
   if (decoder->IsMetadataDecode()) {
+    // If we are animated then the first frame rect is either: 1) the whole image
+    // if the IDAT chunk is part of the animation 2) the frame rect of the first
+    // fDAT chunk otherwise. If we are not animated then we want to make sure to
+    // call PostHasTransparency in the metadata decode if we need to. So it's okay
+    // to pass IntRect(0, 0, width, height) here for animated images; they will
+    // call with the proper first frame rect in the full decode.
     decoder->CheckForTransparency(decoder->format,
                                   IntRect(0, 0, width, height));
 
     // We have the metadata we're looking for, so we don't need to decode any
     // further.
     decoder->mSuccessfulEarlyFinish = true;
     png_longjmp(decoder->mPNG, 1);
   }
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -473,24 +473,24 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeBinaryOperator(f, expected, ExprType::I32);
       case Expr::I64Add:
       case Expr::I64Sub:
       case Expr::I64Mul:
       case Expr::I64DivS:
       case Expr::I64DivU:
       case Expr::I64RemS:
       case Expr::I64RemU:
+        return f.fail("NYI: i64");
       case Expr::I64And:
       case Expr::I64Or:
       case Expr::I64Xor:
       case Expr::I64Shl:
       case Expr::I64ShrS:
       case Expr::I64ShrU:
-        return f.fail("NYI: i64") &&
-               DecodeBinaryOperator(f, expected, ExprType::I64);
+        return DecodeBinaryOperator(f, expected, ExprType::I64);
       case Expr::F32Add:
       case Expr::F32Sub:
       case Expr::F32Mul:
       case Expr::F32Div:
       case Expr::F32Min:
       case Expr::F32Max:
         return DecodeBinaryOperator(f, expected, ExprType::F32);
       case Expr::F32CopySign:
@@ -520,18 +520,17 @@ DecodeExpr(FunctionDecoder& f, ExprType 
       case Expr::I64LtS:
       case Expr::I64LtU:
       case Expr::I64LeS:
       case Expr::I64LeU:
       case Expr::I64GtS:
       case Expr::I64GtU:
       case Expr::I64GeS:
       case Expr::I64GeU:
-        return f.fail("NYI: i64") &&
-               DecodeComparisonOperator(f, expected, ExprType::I64);
+        return DecodeComparisonOperator(f, expected, ExprType::I64);
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Lt:
       case Expr::F32Le:
       case Expr::F32Gt:
       case Expr::F32Ge:
         return DecodeComparisonOperator(f, expected, ExprType::F32);
       case Expr::F64Eq:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -440,21 +440,21 @@ class FunctionCompiler
         if (inDeadCode())
             return nullptr;
         MMod* ins = MMod::NewAsmJS(alloc(), lhs, rhs, type, unsignd);
         curBlock_->add(ins);
         return ins;
     }
 
     template <class T>
-    MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs)
+    MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
-        T* ins = T::NewAsmJS(alloc(), lhs, rhs);
+        T* ins = T::NewAsmJS(alloc(), lhs, rhs, type);
         curBlock_->add(ins);
         return ins;
     }
 
     template <class T>
     MDefinition* bitwise(MDefinition* op)
     {
         if (inDeadCode())
@@ -781,17 +781,17 @@ class FunctionCompiler
             return true;
         }
 
         MAsmJSLoadFuncPtr* ptrFun;
         if (mg().isAsmJS()) {
             MOZ_ASSERT(IsPowerOfTwo(length));
             MConstant* mask = MConstant::New(alloc(), Int32Value(length - 1));
             curBlock_->add(mask);
-            MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
+            MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask, MIRType_Int32);
             curBlock_->add(maskedIndex);
             ptrFun = MAsmJSLoadFuncPtr::New(alloc(), maskedIndex, globalDataOffset);
             curBlock_->add(ptrFun);
         } else {
             // For wasm code, as a space optimization, the ModuleGenerator does not allocate a
             // table for signatures which do not contain any indirectly-callable functions.
             // However, these signatures may still be called (it is not a validation error)
             // so we instead have a flag alwaysThrow which throws an exception instead of loading
@@ -2221,16 +2221,40 @@ EmitComparison(FunctionCompiler& f, Expr
           case Expr::I32LeS: case Expr::I32LtS: case Expr::I32GeS: case Expr::I32GtS:
           case Expr::I32Eq: case Expr::I32Ne:
             compareType = MCompare::Compare_Int32; break;
           case Expr::I32GeU: case Expr::I32GtU: case Expr::I32LeU: case Expr::I32LtU:
             compareType = MCompare::Compare_UInt32; break;
           default: MOZ_CRASH("impossibru opcode");
         }
         break;
+      case Expr::I64Eq:
+      case Expr::I64Ne:
+      case Expr::I64LeS:
+      case Expr::I64LtS:
+      case Expr::I64LeU:
+      case Expr::I64LtU:
+      case Expr::I64GeS:
+      case Expr::I64GtS:
+      case Expr::I64GeU:
+      case Expr::I64GtU:
+        if (!EmitExpr(f, ExprType::I64, &lhs) || !EmitExpr(f, ExprType::I64, &rhs))
+            return false;
+        switch (expr) {
+          case Expr::I64LeS: case Expr::I64LtS: case Expr::I64GeS: case Expr::I64GtS:
+          case Expr::I64Eq: case Expr::I64Ne:
+            compareType = MCompare::Compare_Int64;
+            break;
+          case Expr::I64GeU: case Expr::I64GtU: case Expr::I64LeU: case Expr::I64LtU:
+            compareType = MCompare::Compare_UInt64;
+            break;
+          default:
+            MOZ_CRASH("unexpected opcode");
+        }
+        break;
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Le:
       case Expr::F32Lt:
       case Expr::F32Ge:
       case Expr::F32Gt:
         if (!EmitExpr(f, ExprType::F32, &lhs) || !EmitExpr(f, ExprType::F32, &rhs))
             return false;
@@ -2247,73 +2271,83 @@ EmitComparison(FunctionCompiler& f, Expr
         compareType = MCompare::Compare_Double;
         break;
       default: MOZ_CRASH("unexpected comparison opcode");
     }
 
     JSOp compareOp;
     switch (expr) {
       case Expr::I32Eq:
+      case Expr::I64Eq:
       case Expr::F32Eq:
       case Expr::F64Eq:
         compareOp = JSOP_EQ;
         break;
       case Expr::I32Ne:
+      case Expr::I64Ne:
       case Expr::F32Ne:
       case Expr::F64Ne:
         compareOp = JSOP_NE;
         break;
       case Expr::I32LeS:
       case Expr::I32LeU:
+      case Expr::I64LeS:
+      case Expr::I64LeU:
       case Expr::F32Le:
       case Expr::F64Le:
         compareOp = JSOP_LE;
         break;
       case Expr::I32LtS:
       case Expr::I32LtU:
+      case Expr::I64LtS:
+      case Expr::I64LtU:
       case Expr::F32Lt:
       case Expr::F64Lt:
         compareOp = JSOP_LT;
         break;
       case Expr::I32GeS:
       case Expr::I32GeU:
+      case Expr::I64GeS:
+      case Expr::I64GeU:
       case Expr::F32Ge:
       case Expr::F64Ge:
         compareOp = JSOP_GE;
         break;
       case Expr::I32GtS:
       case Expr::I32GtU:
+      case Expr::I64GtS:
+      case Expr::I64GtU:
       case Expr::F32Gt:
       case Expr::F64Gt:
         compareOp = JSOP_GT;
         break;
       default: MOZ_CRASH("unexpected comparison opcode");
     }
 
     *def = f.compare(lhs, rhs, compareOp, compareType);
     return true;
 }
 
 template<class T>
 static bool
-EmitBitwise(FunctionCompiler& f, MDefinition** def)
+EmitBitwise(FunctionCompiler& f, ExprType type, MDefinition** def)
 {
     MDefinition* lhs;
-    if (!EmitExpr(f, ExprType::I32, &lhs))
+    if (!EmitExpr(f, type, &lhs))
         return false;
     MDefinition* rhs;
-    if (!EmitExpr(f, ExprType::I32, &rhs))
+    if (!EmitExpr(f, type, &rhs))
         return false;
-    *def = f.bitwise<T>(lhs, rhs);
+    MIRType mirType = ToMIRType(type);
+    *def = f.bitwise<T>(lhs, rhs, mirType);
     return true;
 }
 
-template<>
-bool
-EmitBitwise<MBitNot>(FunctionCompiler& f, MDefinition** def)
+static bool
+EmitBitwiseNot(FunctionCompiler& f, MDefinition** def)
 {
     MDefinition* in;
     if (!EmitExpr(f, ExprType::I32, &in))
         return false;
     *def = f.bitwise<MBitNot>(in);
     return true;
 }
 
@@ -2781,29 +2815,29 @@ EmitExpr(FunctionCompiler& f, ExprType t
         return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
       case Expr::I32Clz:
         return EmitUnary<MClz>(f, ExprType::I32, def);
       case Expr::I32Abs:
         return EmitUnaryMir<MAbs>(f, ExprType::I32, def);
       case Expr::I32Neg:
         return EmitUnaryMir<MAsmJSNeg>(f, ExprType::I32, def);
       case Expr::I32Or:
-        return EmitBitwise<MBitOr>(f, def);
+        return EmitBitwise<MBitOr>(f, ExprType::I32, def);
       case Expr::I32And:
-        return EmitBitwise<MBitAnd>(f, def);
+        return EmitBitwise<MBitAnd>(f, ExprType::I32, def);
       case Expr::I32Xor:
-        return EmitBitwise<MBitXor>(f, def);
+        return EmitBitwise<MBitXor>(f, ExprType::I32, def);
       case Expr::I32Shl:
-        return EmitBitwise<MLsh>(f, def);
+        return EmitBitwise<MLsh>(f, ExprType::I32, def);
       case Expr::I32ShrS:
-        return EmitBitwise<MRsh>(f, def);
+        return EmitBitwise<MRsh>(f, ExprType::I32, def);
       case Expr::I32ShrU:
-        return EmitBitwise<MUrsh>(f, def);
+        return EmitBitwise<MUrsh>(f, ExprType::I32, def);
       case Expr::I32BitNot:
-        return EmitBitwise<MBitNot>(f, def);
+        return EmitBitwiseNot(f, def);
       case Expr::I32LoadMem8S:
         return EmitLoad(f, Scalar::Int8, def);
       case Expr::I32LoadMem8U:
         return EmitLoad(f, Scalar::Uint8, def);
       case Expr::I32LoadMem16S:
         return EmitLoad(f, Scalar::Int16, def);
       case Expr::I32LoadMem16U:
         return EmitLoad(f, Scalar::Uint16, def);
@@ -2820,16 +2854,26 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I32LtS:
       case Expr::I32LeS:
       case Expr::I32GtS:
       case Expr::I32GeS:
       case Expr::I32LtU:
       case Expr::I32LeU:
       case Expr::I32GtU:
       case Expr::I32GeU:
+      case Expr::I64Eq:
+      case Expr::I64Ne:
+      case Expr::I64LtS:
+      case Expr::I64LeS:
+      case Expr::I64LtU:
+      case Expr::I64LeU:
+      case Expr::I64GtS:
+      case Expr::I64GeS:
+      case Expr::I64GtU:
+      case Expr::I64GeU:
       case Expr::F32Eq:
       case Expr::F32Ne:
       case Expr::F32Lt:
       case Expr::F32Le:
       case Expr::F32Gt:
       case Expr::F32Ge:
       case Expr::F64Eq:
       case Expr::F64Ne:
@@ -2846,16 +2890,28 @@ EmitExpr(FunctionCompiler& f, ExprType t
         return EmitAtomicsLoad(f, def);
       case Expr::I32AtomicsStore:
         return EmitAtomicsStore(f, def);
       case Expr::I32AtomicsBinOp:
         return EmitAtomicsBinOp(f, def);
       // I64
       case Expr::I64Const:
         return EmitLiteral(f, ExprType::I64, def);
+      case Expr::I64Or:
+        return EmitBitwise<MBitOr>(f, ExprType::I64, def);
+      case Expr::I64And:
+        return EmitBitwise<MBitAnd>(f, ExprType::I64, def);
+      case Expr::I64Xor:
+        return EmitBitwise<MBitXor>(f, ExprType::I64, def);
+      case Expr::I64Shl:
+        return EmitBitwise<MLsh>(f, ExprType::I64, def);
+      case Expr::I64ShrS:
+        return EmitBitwise<MRsh>(f, ExprType::I64, def);
+      case Expr::I64ShrU:
+        return EmitBitwise<MUrsh>(f, ExprType::I64, def);
       // F32
       case Expr::F32Const:
         return EmitLiteral(f, ExprType::F32, def);
       case Expr::F32Add:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(true), def);
       case Expr::F32Sub:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(false), def);
       case Expr::F32Mul:
@@ -3022,32 +3078,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64Popcnt:
       case Expr::I64Add:
       case Expr::I64Sub:
       case Expr::I64Mul:
       case Expr::I64DivS:
       case Expr::I64DivU:
       case Expr::I64RemS:
       case Expr::I64RemU:
-      case Expr::I64Or:
-      case Expr::I64And:
-      case Expr::I64Xor:
-      case Expr::I64Shl:
-      case Expr::I64ShrU:
-      case Expr::I64ShrS:
-      case Expr::I64Eq:
-      case Expr::I64Ne:
-      case Expr::I64LtS:
-      case Expr::I64LeS:
-      case Expr::I64LtU:
-      case Expr::I64LeU:
-      case Expr::I64GtS:
-      case Expr::I64GeS:
-      case Expr::I64GtU:
-      case Expr::I64GeU:
         MOZ_CRASH("NYI");
       case Expr::Unreachable:
         break;
       case Expr::Limit:
         MOZ_CRASH("Limit");
     }
 
     MOZ_CRASH("unexpected wasm opcode");
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -755,23 +755,25 @@ frontend::CompileScript(ExclusiveContext
     if (sourceObjectOut)
         *sourceObjectOut = compiler.sourceObjectPtr();
 
     return script;
 }
 
 ModuleObject*
 frontend::CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& optionsInput,
-                        SourceBufferHolder& srcBuf, LifoAlloc* alloc)
+                        SourceBufferHolder& srcBuf, LifoAlloc* alloc,
+                        ScriptSourceObject** sourceObjectOut /* = nullptr */)
 {
     MOZ_ASSERT(srcBuf.get());
     MOZ_ASSERT(cx->isJSContext() == (alloc == nullptr));
 
     if (!alloc)
         alloc = &cx->asJSContext()->tempLifoAlloc();
+    MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
 
     CompileOptions options(cx, optionsInput);
     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     options.setIsRunOnce(true);
 
     Rooted<StaticScope*> staticScope(cx, &cx->global()->lexicalScope().staticBlock());
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, staticScope,
                               TraceLogger_ParserCompileModule);
@@ -779,16 +781,20 @@ frontend::CompileModule(ExclusiveContext
     if (!module)
         return nullptr;
 
     // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
     // module is compiled off main thread.
     if (cx->isJSContext() && !ModuleObject::FreezeArrayProperties(cx->asJSContext(), module))
         return nullptr;
 
+    // See the comment about sourceObjectOut above.
+    if (sourceObjectOut)
+        *sourceObjectOut = compiler.sourceObjectPtr();
+
     return module;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -29,17 +29,18 @@ CompileScript(ExclusiveContext* cx, Lifo
               HandleObject scopeChain, Handle<StaticScope*> enclosingStaticScope,
               HandleScript evalCaller, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
               SourceCompressionTask* extraSct = nullptr,
               ScriptSourceObject** sourceObjectOut = nullptr);
 
 ModuleObject*
 CompileModule(ExclusiveContext *cx, const ReadOnlyCompileOptions &options,
-              SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr);
+              SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr,
+              ScriptSourceObject** sourceObjectOut = nullptr);
 
 bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 /*
  * enclosingStaticScope is a static enclosing scope (e.g. a StaticWithScope).
  * Must be null if the enclosing scope is a global.
  */
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -145,16 +145,18 @@ template JSObject* js::Allocate<JSObject
                                                  const Class* clasp);
 
 // Attempt to allocate a new GC thing out of the nursery. If there is not enough
 // room in the nursery or there is an OOM, this method will return nullptr.
 template <AllowGC allowGC>
 JSObject*
 GCRuntime::tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots, const Class* clasp)
 {
+    MOZ_ASSERT(isNurseryAllocAllowed());
+    MOZ_ASSERT(!cx->zone()->usedByExclusiveThread);
     MOZ_ASSERT(!IsAtomsCompartment(cx->compartment()));
     JSObject* obj = nursery.allocateObject(cx, thingSize, nDynamicSlots, clasp);
     if (obj)
         return obj;
 
     if (allowGC && !rt->mainThread.suppressGC) {
         minorGC(cx, JS::gcreason::OUT_OF_NURSERY);
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -703,16 +703,23 @@ class GCRuntime
 #ifdef DEBUG
     bool isAllocAllowed() { return noGCOrAllocationCheck == 0; }
     void disallowAlloc() { ++noGCOrAllocationCheck; }
     void allowAlloc() {
         MOZ_ASSERT(!isAllocAllowed());
         --noGCOrAllocationCheck;
     }
 
+    bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
+    void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
+    void allowNurseryAlloc() {
+        MOZ_ASSERT(!isNurseryAllocAllowed());
+        --noNurseryAllocationCheck;
+    }
+
     bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
     void enterUnsafeRegion() { ++inUnsafeRegion; }
     void leaveUnsafeRegion() {
         MOZ_ASSERT(inUnsafeRegion > 0);
         --inUnsafeRegion;
     }
 
     bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
@@ -1307,16 +1314,17 @@ class GCRuntime
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
 
     size_t noGCOrAllocationCheck;
+    size_t noNurseryAllocationCheck;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperState. */
     PRLock* lock;
     mozilla::DebugOnly<mozilla::Atomic<PRThread*>> lockOwner;
 
     BackgroundAllocTask allocTask;
     GCHelperState helperState;
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -4,21 +4,45 @@ assertEq(wasmEvalText('(module (func (re
 assertEq(wasmEvalText('(module (func (result i32) (i32.const -2147483648)) (export "" 0))')(), -2147483648);
 assertEq(wasmEvalText('(module (func (result i32) (i32.const 4294967295)) (export "" 0))')(), -1);
 
 function testUnary(type, opcode, op, expect) {
   assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect);
 }
 
 function testBinary(type, opcode, lhs, rhs, expect) {
-  assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  if (type === 'i64') {
+    // i64 cannot be imported/exported, so we use a wrapper function.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1)))
+                            (func (result i32) (i64.eq (call 0 (i64.const ${lhs}) (i64.const ${rhs})) (i64.const ${expect})))
+                            (export "" 1))`)(), 1);
+  } else {
+    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  }
 }
 
 function testComparison(type, opcode, lhs, rhs, expect) {
-  assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  if (type === 'i64') {
+    // i64 cannot be imported/exported, so we use a wrapper function.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (param i64) (result i32) (i64.${opcode} (get_local 0) (get_local 1)))
+                            (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
+                            (export "" 1))`)(), expect);
+    // Also test if_else, for the compare-and-branch path.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (param i64) (result i32)
+                              (if_else (i64.${opcode} (get_local 0) (get_local 1))
+                                (i32.const 1)
+                                (i32.const 0)))
+                            (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
+                            (export "" 1))`)(), expect);
+  } else {
+    assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+  }
 }
 
 testUnary('i32', 'clz', 40, 26);
 //testUnary('i32', 'ctz', 40, 0); // TODO: NYI
 //testUnary('i32', 'popcnt', 40, 0); // TODO: NYI
 
 testBinary('i32', 'add', 40, 2, 42);
 testBinary('i32', 'sub', 40, 2, 38);
@@ -51,33 +75,64 @@ testComparison('i32', 'ge_u', 40, 40, 1)
 
 //testBinary('i64', 'add', 40, 2, 42); // TODO: NYI
 //testBinary('i64', 'sub', 40, 2, 38); // TODO: NYI
 //testBinary('i64', 'mul', 40, 2, 80); // TODO: NYI
 //testBinary('i64', 'div_s', -40, 2, -20); // TODO: NYI
 //testBinary('i64', 'div_u', -40, 2, 2147483628); // TODO: NYI
 //testBinary('i64', 'rem_s', 40, -3, 1); // TODO: NYI
 //testBinary('i64', 'rem_u', 40, -3, 40); // TODO: NYI
-//testBinary('i64', 'and', 42, 6, 2); // TODO: NYI
-//testBinary('i64', 'or', 42, 6, 46); // TODO: NYI
-//testBinary('i64', 'xor', 42, 2, 40); // TODO: NYI
-//testBinary('i64', 'shl', 40, 2, 160); // TODO: NYI
-//testBinary('i64', 'shr_s', -40, 2, -10); // TODO: NYI
-//testBinary('i64', 'shr_u', -40, 2, 1073741814); // TODO: NYI
+
+if (getBuildConfiguration().x64) {
+    testBinary('i64', 'and', 42, 6, 2);
+    testBinary('i64', 'or', 42, 6, 46);
+    testBinary('i64', 'xor', 42, 2, 40);
+    testBinary('i64', 'and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
+    testBinary('i64', 'or', "0x8765432112345678", "0xffff0000ffff0000", "0xffff4321ffff5678");
+    testBinary('i64', 'xor', "0x8765432112345678", "0xffff0000ffff0000", "0x789a4321edcb5678");
+    testBinary('i64', 'shl', 40, 2, 160);
+    testBinary('i64', 'shr_s', -40, 2, -10);
+    testBinary('i64', 'shr_u', -40, 2, "0x3ffffffffffffff6");
+    testBinary('i64', 'shl', 0xff00ff, 28, "0xff00ff0000000");
+    testBinary('i64', 'shl', 1, 63, "0x8000000000000000");
+    testBinary('i64', 'shl', 1, 64, 1);
+    testBinary('i64', 'shr_s', "0xff00ff0000000", 28, 0xff00ff);
+    testBinary('i64', 'shr_u', "0x8ffff00ff0000000", 56, 0x8f);
 
-//testComparison('i64', 'eq', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ne', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'le_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'le_u', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'gt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'gt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'ge_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ge_u', 40, 40, 1); // TODO: NYI
+    testComparison('i64', 'eq', 40, 40, 1);
+    testComparison('i64', 'ne', 40, 40, 0);
+    testComparison('i64', 'lt_s', 40, 40, 0);
+    testComparison('i64', 'lt_u', 40, 40, 0);
+    testComparison('i64', 'le_s', 40, 40, 1);
+    testComparison('i64', 'le_u', 40, 40, 1);
+    testComparison('i64', 'gt_s', 40, 40, 0);
+    testComparison('i64', 'gt_u', 40, 40, 0);
+    testComparison('i64', 'ge_s', 40, 40, 1);
+    testComparison('i64', 'ge_u', 40, 40, 1);
+    testComparison('i64', 'eq', "0x400012345678", "0x400012345678", 1);
+    testComparison('i64', 'ne', "0x400012345678", "0x400012345678", 0);
+    testComparison('i64', 'ne', "0x400012345678", "0x500012345678", 1);
+    testComparison('i64', 'eq', "0xffffffffffffffff", "-1", 1);
+    testComparison('i64', 'lt_s', "0x8000000012345678", "0x1", 1);
+    testComparison('i64', 'lt_u', "0x8000000012345678", "0x1", 0);
+    testComparison('i64', 'le_s', "-1", "0", 1);
+    testComparison('i64', 'le_u', "-1", "-1", 1);
+    testComparison('i64', 'gt_s', "1", "0x8000000000000000", 1);
+    testComparison('i64', 'gt_u', "1", "0x8000000000000000", 0);
+    testComparison('i64', 'ge_s', "1", "0x8000000000000000", 1);
+    testComparison('i64', 'ge_u', "1", "0x8000000000000000", 0);
+} else {
+    // Sleeper test: once i64 works on more platforms, remove this if-else.
+    try {
+        testComparison('i64', 'eq', 40, 40, 1);
+        assertEq(0, 1);
+    } catch(e) {
+        assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
+    }
+}
 
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result i32) (i32.clz (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
 
 assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
 assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
--- a/js/src/jit/AlignmentMaskAnalysis.cpp
+++ b/js/src/jit/AlignmentMaskAnalysis.cpp
@@ -60,17 +60,17 @@ AnalyzeAsmHeapAddress(MDefinition* ptr, 
         return;
 
     uint32_t i = op1->toConstant()->toInt32();
     uint32_t m = rhs->toConstant()->toInt32();
     if (!IsAlignmentMask(m) || (i & m) != i)
         return;
 
     // The pattern was matched! Produce the replacement expression.
-    MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs);
+    MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs, MIRType_Int32);
     ptr->block()->insertBefore(ptr->toBitAnd(), and_);
     MInstruction* add = MAdd::NewAsmJS(graph.alloc(), and_, op1, MIRType_Int32);
     ptr->block()->insertBefore(ptr->toBitAnd(), add);
     ptr->replaceAllUsesWith(add);
     ptr->block()->discard(ptr->toBitAnd());
 }
 
 bool
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1890,17 +1890,16 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonBooleanInput:
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
       case Bailout_NonSymbolInput:
       case Bailout_NonSimdBool32x4Input:
       case Bailout_NonSimdInt32x4Input:
       case Bailout_NonSimdFloat32x4Input:
       case Bailout_NonSharedTypedArrayInput:
-      case Bailout_InitialState:
       case Bailout_Debugger:
       case Bailout_UninitializedThis:
       case Bailout_BadDerivedConstructorReturn:
         // Do nothing.
         break;
 
       case Bailout_FirstExecution:
         // Do not return directly, as this was not frequent in the first place,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -840,17 +840,17 @@ IonBuilder::build()
     // Initialize the arguments object slot to undefined if necessary.
     if (info().hasArguments()) {
         MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
         current->add(argsObj);
         current->initSlot(info().argsObjSlot(), argsObj);
     }
 
     // Emit the start instruction, so we can begin real instructions.
-    current->add(MStart::New(alloc(), MStart::StartType_Default));
+    current->add(MStart::New(alloc()));
 
     // Guard against over-recursion. Do this before we start unboxing, since
     // this will create an OSI point that will read the incoming argument
     // values, which is nice to do before their last real use, to minimize
     // register/stack pressure.
     MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
     current->add(check);
     MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
@@ -7579,17 +7579,17 @@ IonBuilder::newOsrPreheader(MBasicBlock*
         ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(info().nlocals() + i);
 
         MOsrValue* osrv = MOsrValue::New(alloc(), entry, offset);
         osrBlock->add(osrv);
         osrBlock->initSlot(slot, osrv);
     }
 
     // Create an MStart to hold the first valid MResumePoint.
-    MStart* start = MStart::New(alloc(), MStart::StartType_Osr);
+    MStart* start = MStart::New(alloc());
     osrBlock->add(start);
 
     // MOsrValue instructions are infallible, so the first MResumePoint must
     // occur after they execute, at the point of the MStart.
     if (!resumeAt(start, loopEntry))
         return nullptr;
 
     // Link the same MResumePoint from the MStart to each MOsrValue.
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -105,19 +105,16 @@ enum BailoutKind
     Bailout_NonSimdBool32x4Input,
     Bailout_NonSimdInt32x4Input,
     Bailout_NonSimdFloat32x4Input,
 
     // Atomic operations require shared memory, bail out if the typed array
     // maps unshared memory.
     Bailout_NonSharedTypedArrayInput,
 
-    // For the initial snapshot when entering a function.
-    Bailout_InitialState,
-
     // We hit a |debugger;| statement.
     Bailout_Debugger,
 
     // |this| used uninitialized in a derived constructor
     Bailout_UninitializedThis,
 
     // Derived constructors must return object or undefined
     Bailout_BadDerivedConstructorReturn,
@@ -217,18 +214,16 @@ BailoutKindString(BailoutKind kind)
       case Bailout_NonSimdBool32x4Input:
         return "Bailout_NonSimdBool32x4Input";
       case Bailout_NonSimdInt32x4Input:
         return "Bailout_NonSimdInt32x4Input";
       case Bailout_NonSimdFloat32x4Input:
         return "Bailout_NonSimdFloat32x4Input";
       case Bailout_NonSharedTypedArrayInput:
         return "Bailout_NonSharedTypedArrayInput";
-      case Bailout_InitialState:
-        return "Bailout_InitialState";
       case Bailout_Debugger:
         return "Bailout_Debugger";
       case Bailout_UninitializedThis:
         return "Bailout_UninitializedThis";
       case Bailout_BadDerivedConstructorReturn:
         return "Bailout_BadDerivedConstructorReturn";
       case Bailout_FirstExecution:
         return "Bailout_FirstExecution";
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1102,16 +1102,24 @@ class LInstructionHelper : public detail
     void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
 #ifdef JS_NUNBOX32
         operands_[index] = alloc.type();
         operands_[index + 1] = alloc.payload();
 #else
         operands_[index] = alloc.value();
 #endif
     }
+    void setInt64Operand(size_t index, const LInt64Allocation& alloc) {
+#if JS_BITS_PER_WORD == 32
+        operands_[index] = alloc.low();
+        operands_[index + 1] = alloc.high();
+#else
+        operands_[index] = alloc.value();
+#endif
+    }
 };
 
 template<size_t Defs, size_t Temps>
 class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
 {
     FixedList<LAllocation> operands_;
 
   public:
@@ -1299,19 +1307,16 @@ class LSnapshot : public TempObject
     }
     void setBailoutId(BailoutId id) {
         MOZ_ASSERT(bailoutId_ == INVALID_BAILOUT_ID);
         bailoutId_ = id;
     }
     BailoutKind bailoutKind() const {
         return bailoutKind_;
     }
-    void setBailoutKind(BailoutKind kind) {
-        bailoutKind_ = kind;
-    }
     void rewriteRecoveredInput(LUse input);
 };
 
 struct SafepointSlotEntry {
     // Flag indicating whether this is a slot in the stack or argument space.
     uint32_t stack:1;
 
     // Byte offset of the slot, as in LStackSlot or LArgument.
@@ -1820,18 +1825,16 @@ class LIRGraph
     size_t numConstants() const {
         return constantPool_.length();
     }
     Value* constantPool() {
         return &constantPool_[0];
     }
     void setEntrySnapshot(LSnapshot* snapshot) {
         MOZ_ASSERT(!entrySnapshot_);
-        MOZ_ASSERT(snapshot->bailoutKind() == Bailout_InitialState);
-        snapshot->setBailoutKind(Bailout_ArgumentCheck);
         entrySnapshot_ = snapshot;
     }
     LSnapshot* entrySnapshot() const {
         MOZ_ASSERT(entrySnapshot_);
         return entrySnapshot_;
     }
     bool noteNeedsSafepoint(LInstruction* ins);
     size_t numNonCallSafepoints() const {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -834,16 +834,29 @@ LIRGenerator::visitTest(MTest* test)
             else
                 rhs = useRegister(right);
             LCompareAndBranch* lir = new(alloc()) LCompareAndBranch(comp, op, lhs, rhs,
                                                                     ifTrue, ifFalse);
             add(lir, test);
             return;
         }
 
+        // Compare and branch Int64.
+        if (comp->compareType() == MCompare::Compare_Int64 ||
+            comp->compareType() == MCompare::Compare_UInt64)
+        {
+            JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+            LCompare64AndBranch* lir = new(alloc()) LCompare64AndBranch(comp, op,
+                                                                        useInt64Register(left),
+                                                                        useInt64OrConstant(right),
+                                                                        ifTrue, ifFalse);
+            add(lir, test);
+            return;
+        }
+
         // Compare and branch doubles.
         if (comp->isDoubleComparison()) {
             LAllocation lhs = useRegister(left);
             LAllocation rhs = useRegister(right);
             LCompareDAndBranch* lir = new(alloc()) LCompareDAndBranch(comp, lhs, rhs,
                                                                       ifTrue, ifFalse);
             add(lir, test);
             return;
@@ -1064,16 +1077,26 @@ LIRGenerator::visitCompare(MCompare* com
             rhs = useAnyOrConstant(right);
         } else {
             rhs = useRegister(right);
         }
         define(new(alloc()) LCompare(op, lhs, rhs), comp);
         return;
     }
 
+    // Compare Int64.
+    if (comp->compareType() == MCompare::Compare_Int64 ||
+        comp->compareType() == MCompare::Compare_UInt64)
+    {
+        JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+        define(new(alloc()) LCompare64(op, useInt64Register(left), useInt64OrConstant(right)),
+               comp);
+        return;
+    }
+
     // Compare doubles.
     if (comp->isDoubleComparison()) {
         define(new(alloc()) LCompareD(useRegister(left), useRegister(right)), comp);
         return;
     }
 
     // Compare float32.
     if (comp->isFloat32Comparison()) {
@@ -1093,22 +1116,30 @@ LIRGenerator::visitCompare(MCompare* com
 }
 
 void
 LIRGenerator::lowerBitOp(JSOp op, MInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
-    if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+    if (lhs->type() == MIRType_Int32) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int32);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs);
         return;
     }
 
+    if (lhs->type() == MIRType_Int64) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        lowerForALUInt64(new(alloc()) LBitOpI64(op), ins, lhs, rhs);
+        return;
+    }
+
     LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitTypeOf(MTypeOf* ins)
 {
@@ -1192,32 +1223,40 @@ LIRGenerator::visitBitXor(MBitXor* ins)
 }
 
 void
 LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
-    if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+    if (lhs->type() == MIRType_Int32) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int32);
+
         if (ins->type() == MIRType_Double) {
             MOZ_ASSERT(op == JSOP_URSH);
             lowerUrshD(ins->toUrsh());
             return;
         }
 
         LShiftI* lir = new(alloc()) LShiftI(op);
         if (op == JSOP_URSH) {
             if (ins->toUrsh()->fallible())
                 assignSnapshot(lir, Bailout_OverflowInvalidate);
         }
         lowerForShift(lir, ins, lhs, rhs);
         return;
     }
 
+    if (lhs->type() == MIRType_Int64) {
+        MOZ_ASSERT(rhs->type() == MIRType_Int64);
+        lowerForShiftInt64(new(alloc()) LShiftI64(op), ins, lhs, rhs);
+        return;
+    }
+
     MOZ_ASSERT(ins->specialization() == MIRType_None);
 
     if (op == JSOP_URSH) {
         // Result is either int32 or double so we have to use BinaryV.
         lowerBinaryV(JSOP_URSH, ins);
         return;
     }
 
@@ -1703,22 +1742,23 @@ LIRGenerator::visitFromCharCode(MFromCha
     LFromCharCode* lir = new(alloc()) LFromCharCode(useRegister(code));
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitStart(MStart* start)
 {
+    LStart* lir = new(alloc()) LStart;
+
     // Create a snapshot that captures the initial state of the function.
-    LStart* lir = new(alloc()) LStart;
-    assignSnapshot(lir, Bailout_InitialState);
-
-    if (start->startType() == MStart::StartType_Default && lir->snapshot())
+    assignSnapshot(lir, Bailout_ArgumentCheck);
+    if (start->block()->graph().entryBlock() == start->block())
         lirGraph_.setEntrySnapshot(lir->snapshot());
+
     add(lir);
 }
 
 void
 LIRGenerator::visitNop(MNop* nop)
 {
 }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1803,16 +1803,17 @@ MCompare::New(TempAllocator& alloc, MDef
     return new(alloc) MCompare(left, right, op);
 }
 
 MCompare*
 MCompare::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op,
                    CompareType compareType)
 {
     MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
+               compareType == Compare_Int64 || compareType == Compare_UInt64 ||
                compareType == Compare_Double || compareType == Compare_Float32);
     MCompare* comp = new(alloc) MCompare(left, right, op);
     comp->compareType_ = compareType;
     comp->operandMightEmulateUndefined_ = false;
     comp->setResultType(MIRType_Int32);
     return comp;
 }
 
@@ -2468,25 +2469,27 @@ MBinaryBitwiseInstruction::foldUnnecessa
 void
 MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol) ||
         getOperand(1)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Symbol))
     {
         specialization_ = MIRType_None;
     } else {
-        specializeAsInt32();
+        specializeAs(MIRType_Int32);
     }
 }
 
 void
-MBinaryBitwiseInstruction::specializeAsInt32()
-{
-    specialization_ = MIRType_Int32;
-    MOZ_ASSERT(type() == MIRType_Int32);
+MBinaryBitwiseInstruction::specializeAs(MIRType type)
+{
+    MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+    MOZ_ASSERT(this->type() == type);
+
+    specialization_ = type;
 
     if (isBitOr() || isBitAnd() || isBitXor())
         setCommutative();
 }
 
 void
 MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
 {
@@ -3402,94 +3405,94 @@ MTypeOf::cacheInputMaybeCallableOrEmulat
 
     if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
         markInputNotCallableOrEmulatesUndefined();
 }
 
 MBitAnd*
 MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitAnd(left, right);
+    return new(alloc) MBitAnd(left, right, MIRType_Int32);
 }
 
 MBitAnd*
-MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitAnd* ins = new(alloc) MBitAnd(left, right);
-    ins->specializeAsInt32();
+MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitAnd* ins = new(alloc) MBitAnd(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MBitOr*
 MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitOr(left, right);
+    return new(alloc) MBitOr(left, right, MIRType_Int32);
 }
 
 MBitOr*
-MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitOr* ins = new(alloc) MBitOr(left, right);
-    ins->specializeAsInt32();
+MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitOr* ins = new(alloc) MBitOr(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MBitXor*
 MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MBitXor(left, right);
+    return new(alloc) MBitXor(left, right, MIRType_Int32);
 }
 
 MBitXor*
-MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MBitXor* ins = new(alloc) MBitXor(left, right);
-    ins->specializeAsInt32();
+MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MBitXor* ins = new(alloc) MBitXor(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MLsh*
 MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MLsh(left, right);
+    return new(alloc) MLsh(left, right, MIRType_Int32);
 }
 
 MLsh*
-MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MLsh* ins = new(alloc) MLsh(left, right);
-    ins->specializeAsInt32();
+MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MLsh* ins = new(alloc) MLsh(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MRsh*
 MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MRsh(left, right);
+    return new(alloc) MRsh(left, right, MIRType_Int32);
 }
 
 MRsh*
-MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MRsh* ins = new(alloc) MRsh(left, right);
-    ins->specializeAsInt32();
+MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MRsh* ins = new(alloc) MRsh(left, right, type);
+    ins->specializeAs(type);
     return ins;
 }
 
 MUrsh*
 MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
 {
-    return new(alloc) MUrsh(left, right);
+    return new(alloc) MUrsh(left, right, MIRType_Int32);
 }
 
 MUrsh*
-MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
-    MUrsh* ins = new(alloc) MUrsh(left, right);
-    ins->specializeAsInt32();
+MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+    MUrsh* ins = new(alloc) MUrsh(left, right, type);
+    ins->specializeAs(type);
 
     // Since Ion has no UInt32 type, we use Int32 and we have a special
     // exception to the type rules: we can return values in
     // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
     // without bailing out. This is necessary because Ion has no UInt32
     // type and we can't have bailouts in asm.js code.
     ins->bailoutsDisabled_ = true;
 
@@ -3940,16 +3943,31 @@ MCompare::tryFold(bool* result)
             return true;
         }
         return false;
     }
 
     return false;
 }
 
+template <typename T>
+static bool
+FoldComparison(JSOp op, T left, T right)
+{
+    switch (op) {
+      case JSOP_LT: return left < right;
+      case JSOP_LE: return left <= right;
+      case JSOP_GT: return left > right;
+      case JSOP_GE: return left >= right;
+      case JSOP_STRICTEQ: case JSOP_EQ: return left == right;
+      case JSOP_STRICTNE: case JSOP_NE: return left != right;
+      default: MOZ_CRASH("Unexpected op.");
+    }
+}
+
 bool
 MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
 {
     if (type() != MIRType_Boolean && type() != MIRType_Int32)
         return false;
 
     MDefinition* left = getOperand(0);
     MDefinition* right = getOperand(1);
@@ -4041,106 +4059,41 @@ MCompare::evaluateConstantOperands(TempA
     MConstant* lhs = left->toConstant();
     MConstant* rhs = right->toConstant();
 
     // Fold away some String equality comparisons.
     if (lhs->type() == MIRType_String && rhs->type() == MIRType_String) {
         int32_t comp = 0; // Default to equal.
         if (left != right)
             comp = CompareAtoms(&lhs->toString()->asAtom(), &rhs->toString()->asAtom());
-
-        switch (jsop_) {
-          case JSOP_LT:
-            *result = (comp < 0);
-            break;
-          case JSOP_LE:
-            *result = (comp <= 0);
-            break;
-          case JSOP_GT:
-            *result = (comp > 0);
-            break;
-          case JSOP_GE:
-            *result = (comp >= 0);
-            break;
-          case JSOP_STRICTEQ: // Fall through.
-          case JSOP_EQ:
-            *result = (comp == 0);
-            break;
-          case JSOP_STRICTNE: // Fall through.
-          case JSOP_NE:
-            *result = (comp != 0);
-            break;
-          default:
-            MOZ_CRASH("Unexpected op.");
-        }
-
+        *result = FoldComparison(jsop_, comp, 0);
         return true;
     }
 
     if (compareType_ == Compare_UInt32) {
-        uint32_t lhsUint = uint32_t(lhs->toInt32());
-        uint32_t rhsUint = uint32_t(rhs->toInt32());
-
-        switch (jsop_) {
-          case JSOP_LT:
-            *result = (lhsUint < rhsUint);
-            break;
-          case JSOP_LE:
-            *result = (lhsUint <= rhsUint);
-            break;
-          case JSOP_GT:
-            *result = (lhsUint > rhsUint);
-            break;
-          case JSOP_GE:
-            *result = (lhsUint >= rhsUint);
-            break;
-          case JSOP_STRICTEQ: // Fall through.
-          case JSOP_EQ:
-            *result = (lhsUint == rhsUint);
-            break;
-          case JSOP_STRICTNE: // Fall through.
-          case JSOP_NE:
-            *result = (lhsUint != rhsUint);
-            break;
-          default:
-            MOZ_CRASH("Unexpected op.");
-        }
-
+        *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()), uint32_t(rhs->toInt32()));
+        return true;
+    }
+
+    if (compareType_ == Compare_Int64) {
+        *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
         return true;
     }
 
-    if (!lhs->isTypeRepresentableAsDouble() || !rhs->isTypeRepresentableAsDouble())
-        return false;
-
-    switch (jsop_) {
-      case JSOP_LT:
-        *result = (lhs->numberToDouble() < rhs->numberToDouble());
-        break;
-      case JSOP_LE:
-        *result = (lhs->numberToDouble() <= rhs->numberToDouble());
-        break;
-      case JSOP_GT:
-        *result = (lhs->numberToDouble() > rhs->numberToDouble());
-        break;
-      case JSOP_GE:
-        *result = (lhs->numberToDouble() >= rhs->numberToDouble());
-        break;
-      case JSOP_STRICTEQ: // Fall through.
-      case JSOP_EQ:
-        *result = (lhs->numberToDouble() == rhs->numberToDouble());
-        break;
-      case JSOP_STRICTNE: // Fall through.
-      case JSOP_NE:
-        *result = (lhs->numberToDouble() != rhs->numberToDouble());
-        break;
-      default:
-        return false;
-    }
-
-    return true;
+    if (compareType_ == Compare_UInt64) {
+        *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()), uint64_t(rhs->toInt64()));
+        return true;
+    }
+
+    if (lhs->isTypeRepresentableAsDouble() && rhs->isTypeRepresentableAsDouble()) {
+        *result = FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
+        return true;
+    }
+
+    return false;
 }
 
 MDefinition*
 MCompare::foldsTo(TempAllocator& alloc)
 {
     bool result;
 
     if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1255,37 +1255,19 @@ class MVariadicT : public T
 };
 
 typedef MVariadicT<MInstruction> MVariadicInstruction;
 
 // Generates an LSnapshot without further effect.
 class MStart : public MNullaryInstruction
 {
   public:
-    enum StartType {
-        StartType_Default,
-        StartType_Osr
-    };
-
-  private:
-    StartType startType_;
-
-  private:
-    explicit MStart(StartType startType)
-      : startType_(startType)
-    { }
-
-  public:
     INSTRUCTION_HEADER(Start)
-    static MStart* New(TempAllocator& alloc, StartType startType) {
-        return new(alloc) MStart(startType);
-    }
-
-    StartType startType() {
-        return startType_;
+    static MStart* New(TempAllocator& alloc) {
+        return new(alloc) MStart();
     }
 };
 
 // Instruction marking on entrypoint for on-stack replacement.
 // OSR may occur at loop headers (at JSOP_TRACE).
 // There is at most one MOsrEntry per MIRGraph.
 class MOsrEntry : public MNullaryInstruction
 {
@@ -4360,16 +4342,22 @@ class MCompare
         Compare_Int32,
         Compare_Int32MaybeCoerceBoth,
         Compare_Int32MaybeCoerceLHS,
         Compare_Int32MaybeCoerceRHS,
 
         // Int32 compared as unsigneds
         Compare_UInt32,
 
+        // Int64 compared to Int64.
+        Compare_Int64,
+
+        // Int64 compared as unsigneds.
+        Compare_UInt64,
+
         // Double compared to Double
         Compare_Double,
 
         Compare_DoubleMaybeCoerceLHS,
         Compare_DoubleMaybeCoerceRHS,
 
         // Float compared to Float
         Compare_Float32,
@@ -5564,25 +5552,26 @@ class MToId
     }
 };
 
 class MBinaryBitwiseInstruction
   : public MBinaryInstruction,
     public BitwisePolicy::Data
 {
   protected:
-    MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right)
+    MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryInstruction(left, right), maskMatchesLeftRange(false),
         maskMatchesRightRange(false)
     {
-        setResultType(MIRType_Int32);
-        setMovable();
-    }
-
-    void specializeAsInt32();
+        MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+        setResultType(type);
+        setMovable();
+    }
+
+    void specializeAs(MIRType type);
     bool maskMatchesLeftRange;
     bool maskMatchesRightRange;
 
   public:
     MDefinition* foldsTo(TempAllocator& alloc) override;
     MDefinition* foldUnnecessaryBitop();
     virtual MDefinition* foldIfZero(size_t operand) = 0;
     virtual MDefinition* foldIfNegOne(size_t operand) = 0;
@@ -5605,24 +5594,25 @@ class MBinaryBitwiseInstruction
         return AliasSet::None();
     }
 
     TruncateKind operandTruncateKind(size_t index) const override;
 };
 
 class MBitAnd : public MBinaryBitwiseInstruction
 {
-    MBitAnd(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitAnd)
     static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                             MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(operand); // 0 & x => 0;
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(1 - operand); // x & -1 => x
     }
     MDefinition* foldIfEqual() override {
@@ -5639,24 +5629,25 @@ class MBitAnd : public MBinaryBitwiseIns
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitAnd)
 };
 
 class MBitOr : public MBinaryBitwiseInstruction
 {
-    MBitOr(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitOr(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitOr)
     static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                            MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(operand); // x | -1 => -1
     }
     MDefinition* foldIfEqual() override {
@@ -5671,24 +5662,25 @@ class MBitOr : public MBinaryBitwiseInst
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitOr)
 };
 
 class MBitXor : public MBinaryBitwiseInstruction
 {
-    MBitXor(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MBitXor(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(BitXor)
     static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                             MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         return getOperand(1 - operand); // 0 ^ x => x
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
@@ -5706,18 +5698,18 @@ class MBitXor : public MBinaryBitwiseIns
 
     ALLOW_CLONE(MBitXor)
 };
 
 class MShiftInstruction
   : public MBinaryBitwiseInstruction
 {
   protected:
-    MShiftInstruction(MDefinition* left, MDefinition* right)
-      : MBinaryBitwiseInstruction(left, right)
+    MShiftInstruction(MDefinition* left, MDefinition* right, MIRType type)
+      : MBinaryBitwiseInstruction(left, right, type)
     { }
 
   public:
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
         return this;
@@ -5725,24 +5717,25 @@ class MShiftInstruction
     MDefinition* foldIfAllBitsSet(size_t operand) override {
         return this;
     }
     virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
 };
 
 class MLsh : public MShiftInstruction
 {
-    MLsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right)
+    MLsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(Lsh)
     static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                          MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 << x => 0
         // x << 0 => x
         return getOperand(0);
     }
 
     void computeRange(TempAllocator& alloc) override;
@@ -5751,24 +5744,25 @@ class MLsh : public MShiftInstruction
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MLsh)
 };
 
 class MRsh : public MShiftInstruction
 {
-    MRsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right)
+    MRsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type)
     { }
 
   public:
     INSTRUCTION_HEADER(Rsh)
     static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                          MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 >> x => 0
         // x >> 0 => x
         return getOperand(0);
     }
     void computeRange(TempAllocator& alloc) override;
 
@@ -5779,25 +5773,26 @@ class MRsh : public MShiftInstruction
 
     ALLOW_CLONE(MRsh)
 };
 
 class MUrsh : public MShiftInstruction
 {
     bool bailoutsDisabled_;
 
-    MUrsh(MDefinition* left, MDefinition* right)
-      : MShiftInstruction(left, right),
+    MUrsh(MDefinition* left, MDefinition* right, MIRType type)
+      : MShiftInstruction(left, right, type),
         bailoutsDisabled_(false)
     { }
 
   public:
     INSTRUCTION_HEADER(Ursh)
     static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
-    static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+    static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+                           MIRType type);
 
     MDefinition* foldIfZero(size_t operand) override {
         // 0 >>> x => 0
         if (operand == 0)
             return getOperand(0);
 
         return this;
     }
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -598,18 +598,16 @@ MBasicBlock::shimmySlots(int discardDept
         slots_[stackPosition_ + i] = slots_[stackPosition_ + i + 1];
 
     --stackPosition_;
 }
 
 bool
 MBasicBlock::linkOsrValues(MStart* start)
 {
-    MOZ_ASSERT(start->startType() == MStart::StartType_Osr);
-
     MResumePoint* res = start->resumePoint();
 
     for (uint32_t i = 0; i < stackDepth(); i++) {
         MDefinition* def = slots_[i];
         MInstruction* cloneRp = nullptr;
         if (i == info().scopeChainSlot()) {
             if (def->isOsrScopeChain())
                 cloneRp = def->toOsrScopeChain();
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -688,16 +688,18 @@ class MacroAssembler : public MacroAssem
     inline void and32(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
     inline void and32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
     inline void and32(const Address& src, Register dest) PER_SHARED_ARCH;
 
     inline void andPtr(Register src, Register dest) PER_ARCH;
     inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
 
     inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
+    inline void or64(Imm64 imm, Register64 dest) PER_ARCH;
+    inline void xor64(Imm64 imm, Register64 dest) PER_ARCH;
 
     inline void or32(Register src, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
     inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
 
     inline void orPtr(Register src, Register dest) PER_ARCH;
     inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
 
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -1053,23 +1053,27 @@ IonBuilder::startTrackingOptimizations()
         BytecodeSite* site = maybeTrackedOptimizationSite(current->trackedSite()->pc());
 
         if (!site) {
             site = current->trackedSite();
             site->setOptimizations(new(alloc()) TrackedOptimizations(alloc()));
             // OOMs are handled as if optimization tracking were turned off.
             if (!trackedOptimizationSites_.append(site))
                 site = nullptr;
-        } else {
+        } else if (site->hasOptimizations()) {
             // The same bytecode may be visited multiple times (see
             // restartLoop). Only the last time matters, so clear any previous
             // tracked optimizations.
             site->optimizations()->clear();
         }
 
+        // The case of !site->hasOptimizations() means we had an OOM when
+        // previously attempting to track optimizations. Leave
+        // site->optimizations_ nullptr to leave optimization tracking off.
+
         if (site)
             current->updateTrackedSite(site);
     }
 }
 
 void
 IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType,
                                    TemporaryTypeSet* typeSet)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -628,18 +628,23 @@ Range::Range(const MDefinition* def)
         }
     }
 
     // As a special case, MUrsh is permitted to claim a result type of
     // MIRType_Int32 while actually returning values in [0,UINT32_MAX] without
     // bailouts. If range analysis hasn't ruled out values in
     // (INT32_MAX,UINT32_MAX], set the range to be conservatively correct for
     // use as either a uint32 or an int32.
-    if (!hasInt32UpperBound() && def->isUrsh() && def->toUrsh()->bailoutsDisabled())
+    if (!hasInt32UpperBound() &&
+        def->isUrsh() &&
+        def->toUrsh()->bailoutsDisabled() &&
+        def->type() != MIRType_Int64)
+    {
         lower_ = INT32_MIN;
+    }
 
     assertInvariants();
 }
 
 static uint16_t
 ExponentImpliedByDouble(double d)
 {
     // Handle the special values.
@@ -1311,38 +1316,47 @@ void
 MClampToUint8::computeRange(TempAllocator& alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, 255));
 }
 
 void
 MBitAnd::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::and_(alloc, &left, &right));
 }
 
 void
 MBitOr::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::or_(alloc, &left, &right));
 }
 
 void
 MBitXor::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
     right.wrapAroundToInt32();
 
     setRange(Range::xor_(alloc, &left, &right));
 }
 
@@ -1353,16 +1367,19 @@ MBitNot::computeRange(TempAllocator& all
     op.wrapAroundToInt32();
 
     setRange(Range::not_(alloc, &op));
 }
 
 void
 MLsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MConstant* rhsConst = getOperand(1)->maybeConstantValue();
     if (rhsConst && rhsConst->type() == MIRType_Int32) {
         int32_t c = rhsConst->toInt32();
         setRange(Range::lsh(alloc, &left, c));
@@ -1371,16 +1388,19 @@ MLsh::computeRange(TempAllocator& alloc)
 
     right.wrapAroundToShiftCount();
     setRange(Range::lsh(alloc, &left, &right));
 }
 
 void
 MRsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
     left.wrapAroundToInt32();
 
     MConstant* rhsConst = getOperand(1)->maybeConstantValue();
     if (rhsConst && rhsConst->type() == MIRType_Int32) {
         int32_t c = rhsConst->toInt32();
         setRange(Range::rsh(alloc, &left, c));
@@ -1389,16 +1409,19 @@ MRsh::computeRange(TempAllocator& alloc)
 
     right.wrapAroundToShiftCount();
     setRange(Range::rsh(alloc, &left, &right));
 }
 
 void
 MUrsh::computeRange(TempAllocator& alloc)
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range left(getOperand(0));
     Range right(getOperand(1));
 
     // ursh can be thought of as converting its left operand to uint32, or it
     // can be thought of as converting its left operand to int32, and then
     // reinterpreting the int32 bits as a uint32 value. Both approaches yield
     // the same result. Since we lack support for full uint32 ranges, we use
     // the second interpretation, though it does cause us to be conservative.
@@ -3318,16 +3341,19 @@ MPowHalf::collectRangeInfoPreTrunc()
         operandIsNeverNegativeZero_ = true;
     if (!inputRange.canBeNaN())
         operandIsNeverNaN_ = true;
 }
 
 void
 MUrsh::collectRangeInfoPreTrunc()
 {
+    if (specialization_ == MIRType_Int64)
+        return;
+
     Range lhsRange(lhs()), rhsRange(rhs());
 
     // As in MUrsh::computeRange(), convert the inputs.
     lhsRange.wrapAroundToInt32();
     rhsRange.wrapAroundToShiftCount();
 
     // If the most significant bit of our result is always going to be zero,
     // we can optimize by disabling bailout checks for enforcing an int32 range.
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -159,16 +159,23 @@ LIRGeneratorARM::lowerForALU(LInstructio
     // MulI, but only for bail out paths so useAtStart when no bailouts.
     ins->setOperand(0, ins->snapshot() ? useRegister(lhs) : useRegisterAtStart(lhs));
     ins->setOperand(1, ins->snapshot() ? useRegisterOrConstant(rhs) :
                                          useRegisterOrConstantAtStart(rhs));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 void
+LIRGeneratorARM::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                  MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input)
 {
     ins->setOperand(0, useRegisterAtStart(input));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 template<size_t Temps>
 void
@@ -226,16 +233,23 @@ void
 LIRGeneratorARM::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegister(lhs));
     ins->setOperand(1, useRegisterOrConstant(rhs));
     define(ins, mir);
 }
 
 void
+LIRGeneratorARM::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                    MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::lowerDivI(MDiv* div)
 {
     if (div->isUnsigned()) {
         lowerUDiv(div);
         return;
     }
 
     // Division instructions are slow. Division by constant denominators can be
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -42,16 +42,21 @@ class LIRGeneratorARM : public LIRGenera
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* src);
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -81,16 +81,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
     and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    or32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    or32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xor32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    xor32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -83,16 +83,30 @@ LIRGeneratorARM64::lowerForFPU(LInstruct
 }
 
 template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                                              MDefinition* lhs, MDefinition* rhs);
 template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
                                              MDefinition* lhs, MDefinition* rhs);
 
 void
+LIRGeneratorARM64::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                    MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM64::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                      MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                          MDefinition* lhs, MDefinition* rhs)
 {
     MOZ_CRASH("lowerForBitAndAndBranch");
 }
 
 void
 LIRGeneratorARM64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -43,16 +43,21 @@ class LIRGeneratorARM64 : public LIRGene
     void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
 
     template <size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -91,16 +91,34 @@ MacroAssembler::and64(Imm64 imm, Registe
 {
     vixl::UseScratchRegisterScope temps(this);
     const Register scratch = temps.AcquireX().asUnsized();
     mov(ImmWord(imm.value), scratch);
     andPtr(scratch, dest.reg);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    mov(ImmWord(imm.value), scratch);
+    orPtr(scratch, dest.reg);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    mov(ImmWord(imm.value), scratch);
+    xorPtr(scratch, dest.reg);
+}
+
+void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
     Orr(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
 }
 
 void
 MacroAssembler::or32(Register src, Register dest)
 {
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -50,16 +50,30 @@ LIRGeneratorMIPSShared::lowerForALU(LIns
                                     MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegister(lhs));
     ins->setOperand(1, useRegisterOrConstant(rhs));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 void
+LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                         MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                           MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                                     MDefinition* input)
 {
     ins->setOperand(0, useRegister(input));
     define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
 }
 
 template<size_t Temps>
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -32,16 +32,21 @@ class LIRGeneratorMIPSShared : public LI
                        MDefinition* rhs);
     void lowerUrshD(MUrsh* mir);
 
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                      MDefinition* src);
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
                      MDefinition* lhs, MDefinition* rhs);
 
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     and32(Imm32(imm.value & LOW_32_MASK), dest.low);
     and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    or32(Imm32(imm.value & LOW_32_MASK), dest.low);
+    or32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xor32(Imm32(imm.value & LOW_32_MASK), dest.low);
+    xor32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_or(dest, src);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -46,16 +46,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     ma_li(ScratchRegister, ImmWord(imm.value));
     ma_and(dest.reg, ScratchRegister);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    ma_li(ScratchRegister, ImmWord(imm.value));
+    ma_or(dest.reg, ScratchRegister);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    ma_li(ScratchRegister, ImmWord(imm.value));
+    ma_xor(dest.reg, ScratchRegister);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_or(dest, src);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -33,16 +33,20 @@ class LIRGeneratorNone : public LIRGener
     void lowerForShift(LInstructionHelper<1, 2, 0>*, MDefinition*, MDefinition*, MDefinition*) {
         MOZ_CRASH();
     }
     void lowerUrshD(MUrsh*) { MOZ_CRASH(); }
     template <typename T>
     void lowerForALU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
     template <typename T>
     void lowerForFPU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+    template <typename T>
+    void lowerForALUInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+    template <typename T>
+    void lowerForShiftInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs) {
         MOZ_CRASH();
     }
     void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs) {
         MOZ_CRASH();
     }
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h
+++ b/js/src/jit/shared/CodeGenerator-shared-inl.h
@@ -20,16 +20,26 @@ ToInt32(const LAllocation* a)
 {
     if (a->isConstantValue())
         return a->toConstant()->toInt32();
     if (a->isConstantIndex())
         return a->toConstantIndex()->index();
     MOZ_CRASH("this is not a constant!");
 }
 
+static inline int64_t
+ToInt64(const LAllocation* a)
+{
+    if (a->isConstantValue())
+        return a->toConstant()->toInt64();
+    if (a->isConstantIndex())
+        return a->toConstantIndex()->index();
+    MOZ_CRASH("this is not a constant!");
+}
+
 static inline double
 ToDouble(const LAllocation* a)
 {
     return a->toConstant()->numberToDouble();
 }
 
 static inline Register
 ToRegister(const LAllocation& a)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -2257,16 +2257,86 @@ class LCompare : public LInstructionHelp
     MCompare* mir() {
         return mir_->toCompare();
     }
     const char* extraName() const {
         return CodeName[jsop_];
     }
 };
 
+class LCompare64 : public LInstructionHelper<1, 2 * INT64_PIECES, 0>
+{
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(Compare64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LCompare64(JSOp jsop, const LInt64Allocation& left, const LInt64Allocation& right)
+      : jsop_(jsop)
+    {
+        setInt64Operand(Lhs, left);
+        setInt64Operand(Rhs, right);
+    }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+    MCompare* mir() {
+        return mir_->toCompare();
+    }
+    const char* extraName() const {
+        return CodeName[jsop_];
+    }
+};
+
+class LCompare64AndBranch : public LControlInstructionHelper<2, 2 * INT64_PIECES, 0>
+{
+    MCompare* cmpMir_;
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(Compare64AndBranch)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LCompare64AndBranch(MCompare* cmpMir, JSOp jsop,
+                        const LInt64Allocation& left, const LInt64Allocation& right,
+                        MBasicBlock* ifTrue, MBasicBlock* ifFalse)
+      : cmpMir_(cmpMir), jsop_(jsop)
+    {
+        setInt64Operand(Lhs, left);
+        setInt64Operand(Rhs, right);
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+    MBasicBlock* ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock* ifFalse() const {
+        return getSuccessor(1);
+    }
+    MTest* mir() const {
+        return mir_->toTest();
+    }
+    MCompare* cmpMir() const {
+        return cmpMir_;
+    }
+    const char* extraName() const {
+        return CodeName[jsop_];
+    }
+};
+
 // Compares two integral values of the same JS type, either integer or object.
 // For objects, both operands are in registers.
 class LCompareAndBranch : public LControlInstructionHelper<2, 2, 0>
 {
     MCompare* cmpMir_;
     JSOp jsop_;
 
   public:
@@ -2873,16 +2943,39 @@ class LBitOpI : public LInstructionHelpe
         return CodeName[op_];
     }
 
     JSOp bitop() const {
         return op_;
     }
 };
 
+class LBitOpI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+    JSOp op_;
+
+  public:
+    LIR_HEADER(BitOpI64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    explicit LBitOpI64(JSOp op)
+      : op_(op)
+    { }
+
+    const char* extraName() const {
+        return CodeName[op_];
+    }
+
+    JSOp bitop() const {
+        return op_;
+    }
+};
+
 // Call a VM function to perform a bitwise operation.
 class LBitOpV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(BitOpV)
 
@@ -2926,16 +3019,40 @@ class LShiftI : public LBinaryMath<0>
         return mir_->toInstruction();
     }
 
     const char* extraName() const {
         return CodeName[op_];
     }
 };
 
+class LShiftI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>
+{
+    JSOp op_;
+
+  public:
+    LIR_HEADER(ShiftI64)
+
+    explicit LShiftI64(JSOp op)
+      : op_(op)
+    { }
+
+    JSOp bitop() {
+        return op_;
+    }
+
+    MInstruction* mir() {
+        return mir_->toInstruction();
+    }
+
+    const char* extraName() const {
+        return CodeName[op_];
+    }
+};
+
 class LUrshD : public LBinaryMath<1>
 {
   public:
     LIR_HEADER(UrshD)
 
     LUrshD(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
         setOperand(0, lhs);
         setOperand(1, rhs);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -92,31 +92,35 @@
     _(CreateArgumentsObject)        \
     _(GetArgumentsObjectArg)        \
     _(SetArgumentsObjectArg)        \
     _(ReturnFromCtor)               \
     _(ComputeThis)                  \
     _(BitNotI)                      \
     _(BitNotV)                      \
     _(BitOpI)                       \
+    _(BitOpI64)                     \
     _(BitOpV)                       \
     _(ShiftI)                       \
+    _(ShiftI64)                     \
     _(UrshD)                        \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestFAndBranch)               \
     _(TestVAndBranch)               \
     _(TestOAndBranch)               \
     _(FunctionDispatch)             \
     _(ObjectGroupDispatch)          \
     _(Compare)                      \
     _(CompareAndBranch)             \
+    _(Compare64)                    \
+    _(Compare64AndBranch)           \
     _(CompareD)                     \
     _(CompareDAndBranch)            \
     _(CompareF)                     \
     _(CompareFAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -24,16 +24,17 @@ LIRGeneratorShared::emitAtUses(MInstruct
 }
 
 LUse
 LIRGeneratorShared::use(MDefinition* mir, LUse policy)
 {
     // It is illegal to call use() on an instruction with two defs.
 #if BOX_PIECES > 1
     MOZ_ASSERT(mir->type() != MIRType_Value);
+    MOZ_ASSERT(mir->type() != MIRType_Int64);
 #endif
     ensureDefined(mir);
     policy.setVirtualRegister(mir->virtualRegister());
     return policy;
 }
 
 template <size_t X> void
 LIRGeneratorShared::define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
@@ -82,21 +83,51 @@ LIRGeneratorShared::defineReuseInput(LIn
 
     LDefinition def(type, LDefinition::MUST_REUSE_INPUT);
     def.setReusedInput(operand);
 
     define(lir, mir, def);
 }
 
 template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
+                                          MDefinition* mir, uint32_t operand)
+{
+    // The input should be used at the start of the instruction, to avoid moves.
+    MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
+    MOZ_ASSERT(!lir->isCall());
+
+    uint32_t vreg = getVirtualRegister();
+
+    LDefinition def1(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+    def1.setReusedInput(operand);
+    lir->setDef(0, def1);
+    lir->getDef(0)->setVirtualRegister(vreg);
+
+#if JS_BITS_PER_WORD == 32
+    getVirtualRegister();
+    LDefinition def2(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+    def2.setReusedInput(operand + 1);
+    lir->setDef(1, def2);
+    lir->getDef(1)->setVirtualRegister(vreg + 1);
+#endif
+
+    lir->setMir(mir);
+    mir->setVirtualRegister(vreg);
+    add(lir);
+
+}
+
+template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
                               LDefinition::Policy policy)
 {
     // Call instructions should use defineReturn.
     MOZ_ASSERT(!lir->isCall());
+    MOZ_ASSERT(mir->type() == MIRType_Value);
 
     uint32_t vreg = getVirtualRegister();
 
 #if defined(JS_NUNBOX32)
     lir->setDef(0, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy));
     lir->setDef(1, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy));
     getVirtualRegister();
 #elif defined(JS_PUNBOX64)
@@ -109,16 +140,17 @@ LIRGeneratorShared::defineBox(LInstructi
 }
 
 template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
                                 LDefinition::Policy policy)
 {
     // Call instructions should use defineReturn.
     MOZ_ASSERT(!lir->isCall());
+    MOZ_ASSERT(mir->type() == MIRType_Int64);
 
     uint32_t vreg = getVirtualRegister();
 
 #if JS_BITS_PER_WORD == 32
     lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy));
     lir->setDef(1, LDefinition(vreg + 1, LDefinition::GENERAL, policy));
     getVirtualRegister();
 #else
@@ -659,12 +691,58 @@ LIRGeneratorShared::useBoxOrTypedOrConst
 
 #if defined(JS_NUNBOX32)
     return LBoxAllocation(useRegister(mir), LAllocation());
 #else
     return LBoxAllocation(useRegister(mir));
 #endif
 }
 
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart)
+{
+    MOZ_ASSERT(mir->type() == MIRType_Int64);
+
+    ensureDefined(mir);
+
+    uint32_t vreg = mir->virtualRegister();
+#if JS_BITS_PER_WORD == 32
+    return LInt64Allocation(LUse(vreg + 1, policy, useAtStart),
+                            LUse(vreg, policy, useAtStart));
+#else
+    return LInt64Allocation(LUse(vreg, policy, useAtStart));
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir, bool useAtStart)
+{
+    // On 32-bit platforms, always load the value in registers.
+#if JS_BITS_PER_WORD == 32
+    return useInt64(mir, LUse::REGISTER, useAtStart);
+#else
+    return useInt64(mir, LUse::ANY, useAtStart);
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
+{
+    return useInt64(mir, LUse::REGISTER, useAtStart);
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64OrConstant(MDefinition* mir, bool useAtStart)
+{
+    if (mir->isConstant()) {
+#if defined(JS_NUNBOX32)
+        return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation());
+#else
+        return LInt64Allocation(LAllocation(mir->toConstant()));
+#endif
+    }
+    return useInt64(mir, useAtStart);
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_shared_inl_h */
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -158,24 +158,41 @@ class LIRGeneratorShared : public MDefin
                        LDefinition::Policy policy = LDefinition::REGISTER);
     template <size_t X>
     inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
                        const LDefinition& def);
 
     template <size_t Ops, size_t Temps>
     inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand);
 
+    template <size_t Ops, size_t Temps>
+    inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
+                                      MDefinition* mir, uint32_t operand);
+
     // Returns a box allocation for a Value-typed instruction.
     inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
                                  bool useAtStart = false);
 
     // Returns a box allocation. The use is either typed, a Value, or
     // a constant (if useConstant is true).
     inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
 
+    // Returns an int64 allocation for an Int64-typed instruction.
+    inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
+    inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
+    inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
+    inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
+
+    LInt64Allocation useInt64RegisterAtStart(MDefinition* mir) {
+        return useInt64Register(mir, /* useAtStart = */ true);
+    }
+    LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir) {
+        return useInt64OrConstant(mir, /* useAtStart = */ true);
+    }
+
     // Rather than defining a new virtual register, sets |ins| to have the same
     // virtual register as |as|.
     inline void redefine(MDefinition* ins, MDefinition* as);
 
     // Redefine a sin/cos call to sincos.
     inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
 
     TempAllocator& alloc() const {
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -557,16 +557,25 @@ class Assembler : public AssemblerX86Sha
         masm.shlq_ir(imm.value, dest.encoding());
     }
     void shrq(Imm32 imm, Register dest) {
         masm.shrq_ir(imm.value, dest.encoding());
     }
     void sarq(Imm32 imm, Register dest) {
         masm.sarq_ir(imm.value, dest.encoding());
     }
+    void shlq_cl(Register dest) {
+        masm.shlq_CLr(dest.encoding());
+    }
+    void shrq_cl(Register dest) {
+        masm.shrq_CLr(dest.encoding());
+    }
+    void sarq_cl(Register dest) {
+        masm.sarq_CLr(dest.encoding());
+    }
     void orq(Imm32 imm, Register dest) {
         masm.orq_ir(imm.value, dest.encoding());
     }
     void orq(Register src, Register dest) {
         masm.orq_rr(src.encoding(), dest.encoding());
     }
     void orq(const Operand& src, Register dest) {
         switch (src.kind()) {
@@ -584,16 +593,31 @@ class Assembler : public AssemblerX86Sha
         }
     }
     void xorq(Register src, Register dest) {
         masm.xorq_rr(src.encoding(), dest.encoding());
     }
     void xorq(Imm32 imm, Register dest) {
         masm.xorq_ir(imm.value, dest.encoding());
     }
+    void xorq(const Operand& src, Register dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.xorq_rr(src.reg(), dest.encoding());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.xorq_mr(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            masm.xorq_mr(src.address(), dest.encoding());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
 
     void imulq(Register src, Register dest) {
         masm.imulq_rr(src.encoding(), dest.encoding());
     }
     void vcvtsi2sdq(Register src, FloatRegister dest) {
         masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
     }
 
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -108,16 +108,28 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void orq_mr(const void* addr, RegisterID dst)
     {
         spew("orq        %p, %s", addr, GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
     }
 
+    void xorq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("xorq       " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XOR_GvEv, offset, base, dst);
+    }
+
+    void xorq_mr(const void* addr, RegisterID dst)
+    {
+        spew("xorq       %p, %s", addr, GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_XOR_GvEv, addr, dst);
+    }
+
     void andq_ir(int32_t imm, RegisterID dst)
     {
         spew("andq       $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
             m_formatter.immediate8s(imm);
         } else {
             if (dst == rax)
@@ -222,16 +234,28 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void sarq_CLr(RegisterID dst)
     {
         spew("sarq       %%cl, %s", GPReg64Name(dst));
         m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
     }
 
+    void shlq_CLr(RegisterID dst)
+    {
+        spew("shlq       %%cl, %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHL);
+    }
+
+    void shrq_CLr(RegisterID dst)
+    {
+        spew("shrq       %%cl, %s", GPReg64Name(dst));
+        m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHR);
+    }
+
     void sarq_ir(int32_t imm, RegisterID dst)
     {
         MOZ_ASSERT(imm < 64);
         spew("sarq       $%d, %s", imm, GPReg64Name(dst));
         if (imm == 1)
             m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
         else {
             m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -207,16 +207,126 @@ CodeGeneratorX64::visitCompareBitwiseAnd
     MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
                mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
 
     masm.cmpPtr(lhs.valueReg(), rhs.valueReg());
     emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
 }
 
 void
+CodeGeneratorX64::visitCompare64(LCompare64* lir)
+{
+    MCompare* mir = lir->mir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant())
+        masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+    else
+        masm.cmpPtr(lhs, ToOperand(rhs));
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    masm.emitSet(JSOpToCondition(lir->jsop(), isSigned), ToRegister(lir->output()));
+}
+
+void
+CodeGeneratorX64::visitCompare64AndBranch(LCompare64AndBranch* lir)
+{
+    MCompare* mir = lir->cmpMir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant())
+        masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+    else
+        masm.cmpPtr(lhs, ToOperand(rhs));
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    emitBranch(JSOpToCondition(lir->jsop(), isSigned), lir->ifTrue(), lir->ifFalse());
+}
+
+void
+CodeGeneratorX64::visitBitOpI64(LBitOpI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    switch (lir->bitop()) {
+      case JSOP_BITOR:
+        if (rhs->isConstant())
+            masm.or64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.orq(ToOperand(rhs), lhs);
+        break;
+      case JSOP_BITXOR:
+        if (rhs->isConstant())
+            masm.xor64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.xorq(ToOperand(rhs), lhs);
+        break;
+      case JSOP_BITAND:
+        if (rhs->isConstant())
+            masm.and64(Imm64(ToInt64(rhs)), Register64(lhs));
+        else
+            masm.andq(ToOperand(rhs), lhs);
+        break;
+      default:
+        MOZ_CRASH("unexpected binary opcode");
+    }
+}
+
+void
+CodeGeneratorX64::visitShiftI64(LShiftI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    if (rhs->isConstant()) {
+        int32_t shift = ToInt32(rhs) & 0x3F;
+        switch (lir->bitop()) {
+          case JSOP_LSH:
+            if (shift)
+                masm.shlq(Imm32(shift), lhs);
+            break;
+          case JSOP_RSH:
+            if (shift)
+                masm.sarq(Imm32(shift), lhs);
+            break;
+          case JSOP_URSH:
+            if (shift)
+                masm.shrq(Imm32(shift), lhs);
+            break;
+          default:
+            MOZ_CRASH("Unexpected shift op");
+        }
+    } else {
+        MOZ_ASSERT(ToRegister(rhs) == ecx);
+        switch (lir->bitop()) {
+          case JSOP_LSH:
+            masm.shlq_cl(lhs);
+            break;
+          case JSOP_RSH:
+            masm.sarq_cl(lhs);
+            break;
+          case JSOP_URSH:
+            masm.shrq_cl(lhs);
+            break;
+          default:
+            MOZ_CRASH("Unexpected shift op");
+        }
+    }
+}
+
+void
 CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir)
 {
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -37,16 +37,20 @@ class CodeGeneratorX64 : public CodeGene
   public:
     void visitValue(LValue* value);
     void visitBox(LBox* box);
     void visitUnbox(LUnbox* unbox);
     void visitCompareB(LCompareB* lir);
     void visitCompareBAndBranch(LCompareBAndBranch* lir);
     void visitCompareBitwise(LCompareBitwise* lir);
     void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
+    void visitCompare64(LCompare64* lir);
+    void visitCompare64AndBranch(LCompare64AndBranch* lir);
+    void visitBitOpI64(LBitOpI64* lir);
+    void visitShiftI64(LShiftI64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -39,18 +39,47 @@ void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
     andq(imm, dest);
 }
 
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
-    movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
-    andq(ScratchReg, dest.reg);
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        andq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        andq(scratch, dest.reg);
+    }
+}
+
+void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        orq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        orq(scratch, dest.reg);
+    }
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+        xorq(Imm32(imm.value), dest.reg);
+    } else {
+        ScratchRegisterScope scratch(*this);
+        movq(ImmWord(uintptr_t(imm.value)), scratch);
+        xorq(scratch, dest.reg);
+    }
 }
 
 void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orq(src, dest);
 }
 
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -76,32 +76,66 @@ LIRGeneratorX86Shared::lowerForShift(LIn
         ins->setOperand(1, useOrConstantAtStart(rhs));
     else
         ins->setOperand(1, lhs != rhs ? useFixed(rhs, ecx) : useFixedAtStart(rhs, ecx));
 
     defineReuseInput(ins, mir, 0);
 }
 
 void
+LIRGeneratorX86Shared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                                          MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+
+    // shift operator should be constant or in register ecx
+    // x86 can't shift a non-ecx register
+    if (rhs->isConstant()) {
+        ins->setOperand(INT64_PIECES, useOrConstantAtStart(rhs));
+    } else {
+        // The operands are int64, but we only care about the lower 32 bits of
+        // the RHS. On 32-bit, the code below will load that part in ecx and
+        // will discard the upper half.
+        ensureDefined(rhs);
+        bool useAtStart = (lhs == rhs);
+        LUse use(ecx, useAtStart);
+        use.setVirtualRegister(rhs->virtualRegister());
+        ins->setOperand(INT64_PIECES, use);
+    }
+
+    defineInt64ReuseInput(ins, mir, 0);
+}
+
+void
 LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
                                    MDefinition* input)
 {
     ins->setOperand(0, useRegisterAtStart(input));
     defineReuseInput(ins, mir, 0);
 }
 
 void
 LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
                                    MDefinition* lhs, MDefinition* rhs)
 {
     ins->setOperand(0, useRegisterAtStart(lhs));
     ins->setOperand(1, lhs != rhs ? useOrConstant(rhs) : useOrConstantAtStart(rhs));
     defineReuseInput(ins, mir, 0);
 }
 
+void
+LIRGeneratorX86Shared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+                                        MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+    ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+    ins->setInt64Operand(INT64_PIECES,
+                         lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
+    defineInt64ReuseInput(ins, mir, 0);
+}
+
 template<size_t Temps>
 void
 LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
 {
     // Without AVX, we'll need to use the x86 encodings where one of the
     // inputs must be the same location as the output.
     //
     // :TODO: (Bug 1132894) Note, we might have to allocate a different
--- a/js/src/jit/x86-shared/Lowering-x86-shared.h
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.h
@@ -26,16 +26,22 @@ class LIRGeneratorX86Shared : public LIR
     void visitGuardShape(MGuardShape* ins);
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitPowHalf(MPowHalf* ins);
     void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                        MDefinition* rhs);
     void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
     void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
                      MDefinition* rhs);
+
+    void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+                          MDefinition* lhs, MDefinition* rhs);
+    void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+                            MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
     template<size_t Temps>
     void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs,
                      MDefinition* rhs);
     void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
                          MDefinition* lhs, MDefinition* rhs);
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     andl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
     andl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
 }
 
 void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+    orl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    orl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+    xorl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+    xorl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     orl(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1213,16 +1213,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
 #endif
     fullCompartmentChecks(false),
     mallocBytesUntilGC(0),
     mallocGCTriggered(false),
     alwaysPreserveCode(false),
 #ifdef DEBUG
     inUnsafeRegion(0),
     noGCOrAllocationCheck(0),
+    noNurseryAllocationCheck(0),
 #endif
     lock(nullptr),
     allocTask(rt, emptyChunks_),
     helperState(rt)
 {
     setGCMode(JSGC_MODE_GLOBAL);
 }
 
@@ -7352,16 +7353,27 @@ void JS::AutoAssertNoAlloc::disallowAllo
     gc->disallowAlloc();
 }
 
 JS::AutoAssertNoAlloc::~AutoAssertNoAlloc()
 {
     if (gc)
         gc->allowAlloc();
 }
+
+AutoAssertNoNurseryAlloc::AutoAssertNoNurseryAlloc(JSRuntime* rt)
+  : gc(rt->gc)
+{
+    gc.disallowNurseryAlloc();
+}
+
+AutoAssertNoNurseryAlloc::~AutoAssertNoNurseryAlloc()
+{
+    gc.allowNurseryAlloc();
+}
 #endif
 
 JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject* obj)
   : AutoSuppressGCAnalysis()
 {
     MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapCollecting());
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1349,16 +1349,29 @@ class ZoneList
 
     ZoneList(const ZoneList& other) = delete;
     ZoneList& operator=(const ZoneList& other) = delete;
 };
 
 JSObject*
 NewMemoryStatisticsObject(JSContext* cx);
 
+struct MOZ_RAII AutoAssertNoNurseryAlloc
+{
+#ifdef DEBUG
+    explicit AutoAssertNoNurseryAlloc(JSRuntime* rt);
+    ~AutoAssertNoNurseryAlloc();
+
+  private:
+    gc::GCRuntime& gc;
+#else
+    explicit AutoAssertNoNurseryAlloc(JSRuntime* rt) {}
+#endif
+};
+
 } /* namespace gc */
 
 #ifdef DEBUG
 /* Use this to avoid assertions when manipulating the wrapper map. */
 class MOZ_RAII AutoDisableProxyCheck
 {
     gc::GCRuntime& gc;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1925,55 +1925,76 @@ UncompressedSourceCache::sizeOfExcluding
         }
     }
     return n;
 }
 
 const char16_t*
 ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder)
 {
-    switch (dataType) {
-      case DataUncompressed:
-        return uncompressedChars();
-
-      case DataCompressed: {
-        if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(this, holder))
+    struct CharsMatcher
+    {
+        using ReturnType = const char16_t*;
+
+        JSContext* cx;
+        ScriptSource& ss;
+        UncompressedSourceCache::AutoHoldEntry& holder;
+
+        explicit CharsMatcher(JSContext* cx, ScriptSource& ss,
+                              UncompressedSourceCache::AutoHoldEntry& holder)
+          : cx(cx)
+          , ss(ss)
+          , holder(holder)
+        { }
+
+        ReturnType match(Uncompressed& u) {
+            return u.chars;
+        }
+
+        ReturnType match(Compressed& c) {
+            if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss, holder))
+                return decompressed;
+
+            const size_t nbytes = sizeof(char16_t) * (ss.length() + 1);
+            char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
+            if (!decompressed) {
+                JS_ReportOutOfMemory(cx);
+                return nullptr;
+            }
+
+            if (!DecompressString((const unsigned char*) ss.compressedData(), ss.compressedBytes(),
+                                  reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
+                JS_ReportOutOfMemory(cx);
+                js_free(decompressed);
+                return nullptr;
+            }
+
+            decompressed[ss.length()] = 0;
+
+            if (!cx->runtime()->uncompressedSourceCache.put(&ss, decompressed, holder)) {
+                JS_ReportOutOfMemory(cx);
+                js_free(decompressed);
+                return nullptr;
+            }
+
             return decompressed;
-
-        const size_t nbytes = sizeof(char16_t) * (length_ + 1);
-        char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
-        if (!decompressed) {
-            JS_ReportOutOfMemory(cx);
+        }
+
+        ReturnType match(Parent& p) {
+            return p.parent->chars(cx, holder);
+        }
+
+        ReturnType match(Missing&) {
+            MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
             return nullptr;
         }
-
-        if (!DecompressString((const unsigned char*) compressedData(), compressedBytes(),
-                              reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
-            JS_ReportOutOfMemory(cx);
-            js_free(decompressed);
-            return nullptr;
-        }
-
-        decompressed[length_] = 0;
-
-        if (!cx->runtime()->uncompressedSourceCache.put(this, decompressed, holder)) {
-            JS_ReportOutOfMemory(cx);
-            js_free(decompressed);
-            return nullptr;
-        }
-
-        return decompressed;
-      }
-
-      case DataParent:
-        return parent()->chars(cx, holder);
-
-      default:
-        MOZ_CRASH();
-    }
+    };
+
+    CharsMatcher cm(cx, *this, holder);
+    return data.match(cm);
 }
 
 JSFlatString*
 ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop)
 {
     MOZ_ASSERT(start <= stop);
     UncompressedSourceCache::AutoHoldEntry holder;
     const char16_t* chars = this->chars(cx, holder);
@@ -1991,80 +2012,74 @@ ScriptSource::substringDontDeflate(JSCon
     if (!chars)
         return nullptr;
     return NewStringCopyNDontDeflate<CanGC>(cx, chars + start, stop - start);
 }
 
 void
 ScriptSource::setSource(const char16_t* chars, size_t length, bool ownsChars /* = true */)
 {
-    MOZ_ASSERT(dataType == DataMissing);
-
-    dataType = DataUncompressed;
-    data.uncompressed.chars = chars;
-    data.uncompressed.ownsChars = ownsChars;
-
+    MOZ_ASSERT(data.is<Missing>());
+
+    data = SourceType(Uncompressed(chars, ownsChars));
     length_ = length;
 }
 
 void
 ScriptSource::setCompressedSource(JSRuntime* maybert, void* raw, size_t nbytes, HashNumber hash)
 {
-    MOZ_ASSERT(dataType == DataMissing || dataType == DataUncompressed);
-    if (dataType == DataUncompressed && ownsUncompressedChars())
+    MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
+
+    if (data.is<Uncompressed>() && data.as<Uncompressed>().ownsChars)
         js_free(const_cast<char16_t*>(uncompressedChars()));
 
-    dataType = DataCompressed;
-    data.compressed.raw = raw;
-    data.compressed.nbytes = nbytes;
-    data.compressed.hash = hash;
+    data = SourceType(Compressed(raw, nbytes, hash));
 
     if (maybert)
         updateCompressedSourceSet(maybert);
 }
 
 void
 ScriptSource::updateCompressedSourceSet(JSRuntime* rt)
 {
-    MOZ_ASSERT(dataType == DataCompressed);
+    MOZ_ASSERT(data.is<Compressed>());
     MOZ_ASSERT(!inCompressedSourceSet);
 
     CompressedSourceSet::AddPtr p = rt->compressedSourceSet.lookupForAdd(this);
     if (p) {
         // There is another ScriptSource with the same compressed data.
         // Mark that ScriptSource as the parent and use it for all attempts to
         // get the source for this ScriptSource.
         ScriptSource* parent = *p;
         parent->incref();
 
         js_free(compressedData());
-        dataType = DataParent;
-        data.parent = parent;
+        data = SourceType(Parent(parent));
     } else {
         if (rt->compressedSourceSet.add(p, this))
             inCompressedSourceSet = true;
     }
 }
 
 bool
 ScriptSource::ensureOwnsSource(ExclusiveContext* cx)
 {
-    MOZ_ASSERT(dataType == DataUncompressed);
+    MOZ_ASSERT(data.is<Uncompressed>());
     if (ownsUncompressedChars())
         return true;
 
     char16_t* uncompressed = cx->zone()->pod_malloc<char16_t>(Max<size_t>(length_, 1));
     if (!uncompressed) {
         ReportOutOfMemory(cx);
         return false;
     }
     PodCopy(uncompressed, uncompressedChars(), length_);
 
-    data.uncompressed.chars = uncompressed;
-    data.uncompressed.ownsChars = true;
+    data.as<Uncompressed>().chars = uncompressed;
+    data.as<Uncompressed>().ownsChars = true;
     return true;
 }
 
 bool
 ScriptSource::setSourceCopy(ExclusiveContext* cx, SourceBufferHolder& srcBuf,
                             bool argumentsNotIncluded, SourceCompressionTask* task)
 {
     MOZ_ASSERT(!hasSourceData());
@@ -2164,91 +2179,131 @@ SourceCompressionTask::work()
     if (void* newCompressed = js_realloc(compressed, compressedBytes))
         compressed = newCompressed;
 
     return Success;
 }
 
 ScriptSource::~ScriptSource()
 {
-    MOZ_ASSERT_IF(inCompressedSourceSet, dataType == DataCompressed);
-
-    switch (dataType) {
-      case DataUncompressed:
-        if (ownsUncompressedChars())
-            js_free(const_cast<char16_t*>(uncompressedChars()));
-        break;
-
-      case DataCompressed:
-        // Script source references are only manipulated on the main thread,
-        // except during off thread parsing when the source may be created
-        // and used exclusively by the thread doing the parse. In this case the
-        // ScriptSource might be destroyed while off the main thread, but it
-        // will not have been added to the runtime's compressed source set
-        // until the parse is finished on the main thread.
-        if (inCompressedSourceSet)
-            TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(this);
-        js_free(compressedData());
-        break;
-
-      case DataParent:
-        parent()->decref();
-        break;
-
-      default:
-        break;
-    }
+    struct DestroyMatcher
+    {
+        using ReturnType = void;
+
+        ScriptSource& ss;
+
+        explicit DestroyMatcher(ScriptSource& ss)
+          : ss(ss)
+        { }
+
+        ReturnType match(Uncompressed& u) {
+            if (u.ownsChars)
+                js_free(const_cast<char16_t*>(u.chars));
+        }
+
+        ReturnType match(Compressed& c) {
+            if (ss.inCompressedSourceSet)
+                TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(&ss);
+            js_free(c.raw);
+        }
+
+        ReturnType match(Parent& p) {
+            p.parent->decref();
+        }
+
+        ReturnType match(Missing&) {
+            // Nothing to do here.
+        }
+    };
+
+    MOZ_ASSERT_IF(inCompressedSourceSet, data.is<Compressed>());
+
+    DestroyMatcher dm(*this);
+    data.match(dm);
 }
 
 void
 ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                      JS::ScriptSourceInfo* info) const
 {
-    if (dataType == DataUncompressed && ownsUncompressedChars())
+    if (data.is<Uncompressed>() && ownsUncompressedChars())
         info->uncompressed += mallocSizeOf(uncompressedChars());
-    else if (dataType == DataCompressed)
+    else if (data.is<Compressed>())
         info->compressed += mallocSizeOf(compressedData());
     info->misc += mallocSizeOf(this) +
                   mallocSizeOf(filename_.get()) +
                   mallocSizeOf(introducerFilename_.get());
     info->numScripts++;
 }
 
 template<XDRMode mode>
 bool
 ScriptSource::performXDR(XDRState<mode>* xdr)
 {
+    struct CompressedLengthMatcher
+    {
+        using ReturnType = size_t;
+
+        ReturnType match(Uncompressed&) {
+            return 0;
+        }
+
+        ReturnType match(Compressed& c) {
+            return c.nbytes;
+        }
+
+        ReturnType match(Parent& p) {
+            return p.parent->data.match(*this);
+        }
+
+        ReturnType match(Missing&) {
+            MOZ_CRASH("Missing source data in ScriptSource::performXDR");
+            return 0;
+        }
+    };
+
+    struct RawDataMatcher
+    {
+        using ReturnType = void*;
+
+        ReturnType match(Uncompressed& u) {
+            return (void*) u.chars;
+        }
+
+        ReturnType match(Compressed& c) {
+            return c.raw;
+        }
+
+        ReturnType match(Parent& p) {
+            return p.parent->data.match(*this);
+        }
+
+        ReturnType match(Missing&) {
+            MOZ_CRASH("Missing source data in ScriptSource::performXDR");
+            return nullptr;
+        }
+    };
+
     uint8_t hasSource = hasSourceData();
     if (!xdr->codeUint8(&hasSource))
         return false;
 
     uint8_t retrievable = sourceRetrievable_;
     if (!xdr->codeUint8(&retrievable))
         return false;
     sourceRetrievable_ = retrievable;
 
     if (hasSource && !sourceRetrievable_) {
         if (!xdr->codeUint32(&length_))
             return false;
 
         uint32_t compressedLength;
         if (mode == XDR_ENCODE) {
-            switch (dataType) {
-              case DataUncompressed:
-                compressedLength = 0;
-                break;
-              case DataCompressed:
-                compressedLength = compressedBytes();
-                break;
-              case DataParent:
-                compressedLength = parent()->compressedBytes();
-                break;
-              default:
-                MOZ_CRASH();
-            }
+            CompressedLengthMatcher m;
+            compressedLength = data.match(m);
         }
         if (!xdr->codeUint32(&compressedLength))
             return false;
 
         {
             uint8_t argumentsNotIncluded;
             if (mode == XDR_ENCODE)
                 argumentsNotIncluded = argumentsNotIncluded_;
@@ -2267,30 +2322,18 @@ ScriptSource::performXDR(XDRState<mode>*
             }
 
             if (compressedLength)
                 setCompressedSource(xdr->cx()->runtime(), p, compressedLength,
                                     CompressedSourceHasher::computeHash(p, compressedLength));
             else
                 setSource((const char16_t*) p, length_);
         } else {
-            void* p;
-            switch (dataType) {
-              case DataUncompressed:
-                p = (void*) uncompressedChars();
-                break;
-              case DataCompressed:
-                p = compressedData();
-                break;
-              case DataParent:
-                p = parent()->compressedData();
-                break;
-              default:
-                MOZ_CRASH();
-            }
+            RawDataMatcher rdm;
+            void* p = data.match(rdm);
             if (!xdr->codeBytes(p, byteLen))
                 return false;
         }
     }
 
     uint8_t haveSourceMap = hasSourceMapURL();
     if (!xdr->codeUint8(&haveSourceMap))
         return false;
@@ -2461,19 +2504,19 @@ ScriptSource::setSourceMapURL(ExclusiveC
 
     sourceMapURL_ = DuplicateString(cx, sourceMapURL);
     return sourceMapURL_ != nullptr;
 }
 
 size_t
 ScriptSource::computedSizeOfData() const
 {
-    if (dataType == DataUncompressed && ownsUncompressedChars())
+    if (data.is<Uncompressed>() && ownsUncompressedChars())
         return sizeof(char16_t) * length_;
-    if (dataType == DataCompressed)
+    if (data.is<Compressed>())
         return compressedBytes();
     return 0;
 }
 
 /*
  * Shared script data management.
  */
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -7,16 +7,17 @@
 /* JS script descriptor. */
 
 #ifndef jsscript_h
 #define jsscript_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/Variant.h"
 
 #include "jsatom.h"
 #include "jslock.h"
 #include "jsopcode.h"
 #include "jstypes.h"
 
 #include "gc/Barrier.h"
 #include "gc/Rooting.h"
@@ -601,37 +602,54 @@ class ScriptSource
 
     uint32_t refs;
 
     // Note: while ScriptSources may be compressed off thread, they are only
     // modified by the main thread, and all members are always safe to access
     // on the main thread.
 
     // Indicate which field in the |data| union is active.
-    enum {
-        DataMissing,
-        DataUncompressed,
-        DataCompressed,
-        DataParent
-    } dataType;
-
-    union {
-        struct {
-            const char16_t* chars;
-            bool ownsChars;
-        } uncompressed;
-
-        struct {
-            void* raw;
-            size_t nbytes;
-            HashNumber hash;
-        } compressed;
+
+    struct Missing { };
+
+    struct Uncompressed
+    {
+        Uncompressed(const char16_t* chars, bool ownsChars)
+          : chars(chars)
+          , ownsChars(ownsChars)
+        { }
+
+        const char16_t* chars;
+        bool ownsChars;
+    };
+
+    struct Compressed
+    {
+        Compressed(void* raw, size_t nbytes, HashNumber hash)
+          : raw(raw)
+          , nbytes(nbytes)
+          , hash(hash)
+        { }
+
+        void* raw;
+        size_t nbytes;
+        HashNumber hash;
+    };
+
+    struct Parent
+    {
+        explicit Parent(ScriptSource* parent)
+          : parent(parent)
+        { }
 
         ScriptSource* parent;
-    } data;
+    };
+
+    using SourceType = mozilla::Variant<Missing, Uncompressed, Compressed, Parent>;
+    SourceType data;
 
     uint32_t length_;
 
     // The filename of this script.
     UniqueChars filename_;
 
     UniqueTwoByteChars displayURL_;
     UniqueTwoByteChars sourceMapURL_;
@@ -675,17 +693,17 @@ class ScriptSource
     bool hasIntroductionOffset_:1;
 
     // Whether this is in the runtime's set of compressed ScriptSources.
     bool inCompressedSourceSet:1;
 
   public:
     explicit ScriptSource()
       : refs(0),
-        dataType(DataMissing),
+        data(SourceType(Missing())),
         length_(0),
         filename_(nullptr),
         displayURL_(nullptr),
         sourceMapURL_(nullptr),
         mutedErrors_(false),
         introductionOffset_(0),
         introducerFilename_(nullptr),
         introductionType_(nullptr),
@@ -704,60 +722,54 @@ class ScriptSource
     }
     bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options);
     bool setSourceCopy(ExclusiveContext* cx,
                        JS::SourceBufferHolder& srcBuf,
                        bool argumentsNotIncluded,
                        SourceCompressionTask* tok);
     void setSourceRetrievable() { sourceRetrievable_ = true; }
     bool sourceRetrievable() const { return sourceRetrievable_; }
-    bool hasSourceData() const { return dataType != DataMissing; }
-    bool hasCompressedSource() const { return dataType == DataCompressed; }
+    bool hasSourceData() const { return !data.is<Missing>(); }
+    bool hasCompressedSource() const { return data.is<Compressed>(); }
     size_t length() const {
         MOZ_ASSERT(hasSourceData());
         return length_;
     }
     bool argumentsNotIncluded() const {
         MOZ_ASSERT(hasSourceData());
         return argumentsNotIncluded_;
     }
     const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp);
     JSFlatString* substring(JSContext* cx, uint32_t start, uint32_t stop);
     JSFlatString* substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop);
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 JS::ScriptSourceInfo* info) const;
 
     const char16_t* uncompressedChars() const {
-        MOZ_ASSERT(dataType == DataUncompressed);
-        return data.uncompressed.chars;
+        return data.as<Uncompressed>().chars;
     }
 
     bool ownsUncompressedChars() const {
-        MOZ_ASSERT(dataType == DataUncompressed);
-        return data.uncompressed.ownsChars;
+        return data.as<Uncompressed>().ownsChars;
     }
 
     void* compressedData() const {
-        MOZ_ASSERT(dataType == DataCompressed);
-        return data.compressed.raw;
+        return data.as<Compressed>().raw;
     }
 
     size_t compressedBytes() const {
-        MOZ_ASSERT(dataType == DataCompressed);
-        return data.compressed.nbytes;
+        return data.as<Compressed>().nbytes;
     }
 
     HashNumber compressedHash() const {
-        MOZ_ASSERT(dataType == DataCompressed);
-        return data.compressed.hash;
+        return data.as<Compressed>().hash;
     }
 
     ScriptSource* parent() const {
-        MOZ_ASSERT(dataType == DataParent);
-        return data.parent;
+        return data.as<Parent>().parent;
     }
 
     void setSource(const char16_t* chars, size_t length, bool ownsChars = true);
     void setCompressedSource(JSRuntime* maybert, void* raw, size_t nbytes, HashNumber hash);
     void updateCompressedSourceSet(JSRuntime* rt);
     bool ensureOwnsSource(ExclusiveContext* cx);
 
     // XDR handling
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -280,17 +280,18 @@ ModuleParseTask::ModuleParseTask(Exclusi
               callbackData)
 {
 }
 
 void
 ModuleParseTask::parse()
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc);
+    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc,
+                                                   sourceObject.address());
     if (module)
         script = module->script();
 }
 
 void
 js::CancelOffThreadParses(JSRuntime* rt)
 {
     AutoLockHelperThreadState lock;
@@ -455,16 +456,17 @@ QueueOffThreadParseTask(JSContext* cx, P
 bool
 js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
                               const char16_t* chars, size_t length,
                               JS::OffThreadCompileCallback callback, void* callbackData)
 {
     // Suppress GC so that calls below do not trigger a new incremental GC
     // which could require barriers on the atoms compartment.
     gc::AutoSuppressGC nogc(cx);
+    gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
 
     JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
     if (!global)
         return false;
 
     ScopedJSDeletePtr<ExclusiveContext> helpercx(
         cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
                                    ExclusiveContext::Context_Exclusive));
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -76,24 +76,20 @@ interface nsIXPCScriptable : nsISupports
 
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
     boolean addProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in jsval val);
 
-    // The returnCode should be set to NS_SUCCESS_I_DID_SOMETHING if
-    // this method does something.
     boolean getProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
-    // The returnCode should be set to NS_SUCCESS_I_DID_SOMETHING if
-    // this method does something.
     boolean setProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
     boolean enumerate(in nsIXPConnectWrappedNative wrapper,
                      in JSContextPtr cx, in JSObjectPtr obj);
 
     boolean newEnumerate(in nsIXPConnectWrappedNative wrapper,
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2046,16 +2046,49 @@ nsPresContext::PostRebuildAllStyleDataEv
 {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
   RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
 }
 
+struct MediaFeatureHints
+{
+  nsRestyleHint restyleHint;
+  nsChangeHint changeHint;
+};
+
+static bool
+MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument* aDocument, void* aHints)
+{
+  MediaFeatureHints* hints = static_cast<MediaFeatureHints*>(aHints);
+  if (nsIPresShell* shell = aDocument->GetShell()) {
+    if (nsPresContext* pc = shell->GetPresContext()) {
+      pc->MediaFeatureValuesChangedAllDocuments(hints->restyleHint,
+                                                hints->changeHint);
+    }
+  }
+  return true;
+}
+
+void
+nsPresContext::MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
+                                                     nsChangeHint aChangeHint)
+{
+    MediaFeatureValuesChanged(aRestyleHint, aChangeHint);
+    MediaFeatureHints hints = {
+      aRestyleHint,
+      aChangeHint
+    };
+
+    mDocument->EnumerateSubDocuments(MediaFeatureValuesChangedAllDocumentsCallback,
+                                     &hints);
+}
+
 void
 nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
                                          nsChangeHint aChangeHint)
 {
   mPendingMediaFeatureValuesChanged = false;
 
   // MediumFeaturesChanged updates the applied rules, so it always gets called.
   if (mShell) {
@@ -2152,16 +2185,35 @@ nsPresContext::HandleMediaFeatureValuesC
 {
   // Null-check mShell in case the shell has been destroyed (and the
   // event is the only thing holding the pres context alive).
   if (mPendingMediaFeatureValuesChanged && mShell) {
     MediaFeatureValuesChanged(nsRestyleHint(0));
   }
 }
 
+static void
+NotifyTabSizeModeChanged(TabParent* aTab, void* aArg)
+{
+  nsSizeMode* sizeMode = static_cast<nsSizeMode*>(aArg);
+  aTab->SizeModeChanged(*sizeMode);
+}
+
+void
+nsPresContext::SizeModeChanged(nsSizeMode aSizeMode)
+{
+  if (HasCachedStyleData()) {
+    nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
+                                            NotifyTabSizeModeChanged,
+                                            &aSizeMode);
+    MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
+                                          NS_STYLE_HINT_REFLOW);
+  }
+}
+
 nsCompatibility
 nsPresContext::CompatibilityMode() const
 {
   return Document()->GetCompatibilityMode();
 }
 
 void
 nsPresContext::SetPaginatedScrolling(bool aPaginated)
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -16,16 +16,17 @@
 #include "nsIPresShell.h"
 #include "nsRect.h"
 #include "nsFont.h"
 #include "gfxFontConstants.h"
 #include "nsIAtom.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsCRT.h"
+#include "nsIWidgetListener.h"
 #include "FramePropertyTable.h"
 #include "nsGkAtoms.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsChangeHint.h"
 #include <algorithm>
 // This also pulls in gfxTypes.h, which we cannot include directly.
 #include "gfxRect.h"
 #include "nsTArray.h"
@@ -274,24 +275,39 @@ public:
    *    rerunning of selector matching
    *
    * For aChangeHint, see RestyleManager::RebuildAllStyleData.  (Passing
    * a nonzero aChangeHint forces rebuilding style data even if
    * nsRestyleHint(0) is passed.)
    */
   void MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
                                  nsChangeHint aChangeHint = nsChangeHint(0));
+  /**
+   * Calls MediaFeatureValuesChanged for this pres context and all descendant
+   * subdocuments that have a pres context. This should be used for media
+   * features that must be updated in all subdocuments e.g. display-mode.
+   */
+  void MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
+                                             nsChangeHint aChangeHint = nsChangeHint(0));
+
   void PostMediaFeatureValuesChangedEvent();
   void HandleMediaFeatureValuesChangedEvent();
   void FlushPendingMediaFeatureValuesChanged() {
     if (mPendingMediaFeatureValuesChanged)
       MediaFeatureValuesChanged(nsRestyleHint(0));
   }
 
   /**
+   * Updates the size mode on all remote children and recursively notifies this
+   * document and all subdocuments (including remote children) that a media
+   * feature value has changed.
+   */
+  void SizeModeChanged(nsSizeMode aSizeMode);
+
+  /**
    * Access compatibility mode for this context.  This is the same as
    * our document's compatibility mode.
    */
   nsCompatibility CompatibilityMode() const;
 
   /**
    * Notify the context that the document's compatibility mode has changed
    */
--- a/layout/generic/nsBRFrame.cpp
+++ b/layout/generic/nsBRFrame.cpp
@@ -262,12 +262,19 @@ BRFrame::AccessibleType()
   nsIContent *parent = mContent->GetParent();
   if (parent && parent->IsRootOfNativeAnonymousSubtree() &&
       parent->GetChildCount() == 1) {
     // This <br> is the only node in a text control, therefore it is the hacky
     // "bogus node" used when there is no text in the control
     return a11y::eNoType;
   }
 
+  // Trailing HTML br element don't play any difference. We don't need to expose
+  // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
+  // for details).
+  if (!mContent->GetNextSibling() && !GetNextSibling()) {
+    return a11y::eNoType;
+  }
+
   return a11y::eHTMLBRType;
 }
 #endif
 
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/LICENSE
@@ -0,0 +1,32 @@
+Tests in this directory have been imported from
+https://github.com/MathML/MathMLinHTML5-tests
+
+
+Copyright (c) 2016 MathML Association
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
new file mode 100644
index 0000000000000000000000000000000000000000..0f2ae3bc1261a3930b8e23a1efe8e0b547fe8c70
GIT binary patch
literal 1208
zc$_QucXRU(3GruOVA;XI%>V?P4GfG7Sb?*fn*sv^a|n=mj)8$u>gT`dU2bk6zCgVy
zKru-mmMK3LTj1*$;sF$M0P^#J>LQrfv>*Bh>l=aCKruTYPT=Pf_>i2NSO7GS1;{rC
z$~iIcG9SxGO-uo@Ie=nXKy1dcbdzTWP!K3)0^~~ru?mwv>-~(}k_w<58-RR}S`PLL
z3=Fx66+rVC1Au%sFlGv1$V<#k1&Yl8;w+$grjT0&(gpd&B|v*^fNI2mSmA#ggK@>2
z<b;%j1cpatyT5*a^fPy#QGxM(hAx8`t3AoWM!c*tJU$9s46zbipBNaRYX2Pi(W=<>
zla-mpz-R%J8bg!~GsuZR>zN>G66+3HG&W9cY+Tu$$<NH}%-@n$b0Fcw2^Nvmgr_MD
zjBact7Y-}{!XK?4xt&GYq!#cnD>G=yaSDO-AD!r*b;v>F*#0B-7kpm|rYX;GN>Sr>
z4HsS6y-1q-owLN*1rnZ0FGQ<)-&J>i<GA~c%hqYX&#}DdjDtLhb;h6fecxNj{6I?o
zLqNUk8JTL156^D2e^5Hkd#75yZ#KuD$&bbI{)8XTW$BG}t2(Q_PHxSrB!2EEeYJNU
zUM^(zzqk0YnpoZOLheKLWp|$avsOE?;do~x&oz%grv=?DTe(~&1iYC1tfO*L(Y!)a
zw}TT6Pu-|>zQ;H@r~cGgD_vJ}P8XHX_%n}<=AJ*YLu1pCBTG+8Y+DrW^EOhvs8mJ#
z%=-PSB`)liQe4b`<I|E)=2vo8T-?U=G-<=B$4k8Aj=2Xf*|jQBVJXk&!(r1`dYW0s
ziga23*nR!HSa9{)OXn<gx8$bwCp8NG|DW$LH$3q~#?k+O3TCA3E}Hc;TX)es5#^7s
z-3-HcokhdeSG-h@4w@YtUCq5ZeB1o5cQ;<CJQ(8Sb#-;t{(^(?W-Ct0LE<VQA*~@n
zlFf}xU<2bPiGwv_Y^(}rnOYR~9s2)Y;M0HS6<%y^W^59|>Iy3MR~Q*06WK0<^`$X1
z|3Ay)=Fl~fk(Yr%Bb0F`*u0d4#KeS@lq+7^UOsv!&z!&PyXJ(>IghhG=QRw?4Gb>m
zYMu4eJ$b_C+&OPuJu{Xr0|O&N1EY-p57K`qp8et6_Oe~)Ge74vIk{tb3JFsaE6(X3
zKA@m*_=8tKTcdMZphrie<Pyi;gYy?2vfRI56GzIHCbvz?nkF_boY|D0<k_~qzbUz}
zu&yXg=>)6G?v@I5W=3UZVdce>ioV}_zqMNO`@6q8Q}sW`ZHyPXFaK|H!tLMHdtZFN
zH9xxU`linx|E>~zDfaf+Lnemw57Ad);giD9<Xx7(<KUNvva=IR7<^xFurt8(!h@uW
z11pXT97s4^)y~MXL|LMPS$)A4HNmXqh6V=51T*yn4E7($V`~-^QQmyfV6g`C;rpu{
zw(&BvH%m+iW4zRpAS`iEVCr(FBQ`uvJVHE07IT>cHJL&dGNpZNJlooFC_{yVp=mP@
L7X$MP1~3KyHR#Cl
new file mode 100644
index 0000000000000000000000000000000000000000..03e9b3688596fd63753844ee5baa11c5c3e9a9fd
GIT binary patch
literal 1264
zc$_QucXRU(3GruOVEMqn%>V>kR~Q%>umWc{Hw6X;<{BXLAp-+r@H?R&$!=~TzCgVR
zKru-mmMK3LTj1*$;sF!`smlSXi(q0?5%dq%Hv+MNVs=2Bz|SS{Avrg(0BD{LkZ%r@
zb7J6SK9-T1m;z*T0L8R`*o<ZACeI9@AW+N%$d?9U6()by`x&_<6+k;)0Qn%b9PAeu
z7;+OUfaWm<0QqcS%oM<omzbLh6q^CWlNlIT<(IKfpIne%TmrNgXb*!J5G(v|V=%6m
zlbn!}kihWBe)sg5M?c@LNqvx3$0(Axqom@vT8b2xg~SX85r(W_Zes=psM<e=ezYof
z{bXfkF)&)dq{a|s!whmF(0V3_n#8(;7LAQl8yi=4XYw;MJM*`s)f`ATae_r8HQ{MW
z1EU*T$%O+8fbd7_M{Z}4HmL<X%*qU!a-2dS{a1s|`aL#~m}A1)$^A&M#&J=5Na4?k
z4to`r$OTlp#w@<#tK%ed)6K+x!}D4my^QnK_kY`+ush-x^O;GZvEymY<MW?;kJ~fw
zee7b{{a)g9hwzz?`i}(tCeIgKzC+&D-R79h-rfp+_m|#Be$VJXf5PV&yUg9TRgCI4
z4!PMJQk&^A=TZ14^+$%=7Vj5)@3_QX>E3a(zrE5DB`;Pk3S8LMet^@<M<V@%Vxi*}
zr-L(R6`s6kDl=QryM2#ZUZeEouVLX^#nqp>oGGeQ3~OB<?p^qHj%Mk@t%|vaw{l(E
zcQrM_*-*;H=rWtD^SN2d?)kjNwiBQ4SKRz)NB5m#E14*_f59_kW3pD+7lg6SH!9k&
z(bY2I#I;qX{kwGcY|MVUYuSsr@1;XUWj8;soR|J>W#GPRPn}oKnSAEKW}khhZ!5_x
z;lEgvE2@|8W!tFzZN1f}yRU?%r+9zc&feOZ`S;Kbug;q$;^B{!!w(l#&ODh@QE>lT
z>D|C{L3!y>ES{2i1?7D^g0BU}KG`{kZ}z`+)_?iK4oY)^<1`^5tsy~@&5cc91LG%&
zgEeAotO{qDS`_vj`u|_x(|_j`UTkh=Y!bri3M%$j7#SiH*)D_ir7<-BKg;9h&^3{f
zmw`belyN6Gjie+bCMKk$Z1L6h^3gka=KN(}&$Fk`pFQKPb5h^yq>kPR&ow%FE6;fP
z`s*-s&61Lkcp*0R*^+4~DQfA@mx_r?Gr3Ag%#@Ij+*1F+_|Js2KZ=LHI8Xc8&f?9_
zpKLC$;go^ahtmcI948F4xK9}roG_^3HZ*89Sz)MRc&7NA{tBH_KIb(8FL+!FF)%VP
z3I5=#bMnL2zyck;bN=UiSS-7Qjt4$5Q~)OSq?C}LclUnpEz^H|Z?FCKH>vluzj;6U
zeYjpIr@DT<_}{qS#i!p#mD%l!H&wd3=&nr+!>1F)><kRhFiT-*@-EBYaq!DS+1Uvu
z48AWoU~<5G^dPC?z>4Dn2NDifwKtj+FnMJQEi98_W=@v*=w`gX;ld%=6=?<%j)q3k
zyvAKd27mT)rQ9|sFz8^+6jeCOVb&<f6IiL`aOSYW5ru;a?UyYDrYbpzY9)xt--zkZ
R=y7WT7B)Oa%)pWYgaK!++Moac
new file mode 100644
index 0000000000000000000000000000000000000000..df6aab016ae7fdde75544ae0af1256cc015303eb
GIT binary patch
literal 1280
zc$_QucXRU(3GruOU}a$7W&i?i4hBXBtiajLO@V=dxdX_2%D})FlRSfcvYT6oF9QQh
z0Z>d5h-J!;#TNKFhIjzQdVu^Kpt=YqHuazW!TLrZHc-qCh!gm^1U@9^CIa;_TLAgy
zKshG{Ugl#Nsfj5-HV05l3y95FmTvOQ015)dOn`i8AXZ`WXT6`1TT%hE;|GurQp>@9
zfq@}6u>xowV*rrP2F6SQ40(yUsX(zAKs*oVN1uW<JpBdv#U(&{L2ecUVuk;048|36
zk`q!A5*QxM>d~}*^fPy#QGxM(hAx8`t3AoWM!c*tJU$9s4B=H=KzpES{~Y?!s@V0D
zm6^rBXaSQNLzE3O$caGfnILKs>ke8pHcoA9T-lw;&&=%1-;!2yAmPLb7LnA1rzs7L
zZfqqN4lDq|AFUs`okiND7Vt1DGib_j3W4-r4Knn5VjxmGlgGoPLwSc|lhWdfE{_X6
z0+&QGmMwLR()6+s&UUid)Eh8sCsX(W_gh8z8^4xSH@>wvxUb?2s{@nZ>5rek|2}{I
zA6r4se2)$P{Wf-39OPLq*dL&BUU2yiX<JvDV>)+TY8obgDL>Laqqly-%$|OW-JFrk
zejA#5D_YEEy3KhM{z?6jVcFt(L3xKI`hxOF)Bhbc<=OUP<)V#l%>0eUAr{XqT=_bi
zGCC(|^B!kS4=>1R(~*5NYX|eXv)i^;-K{7Lo7<hK$GuUfc5_UJ_H&=5-s^cYwcmvr
zeb?1@<~@4x!Murj8d61B=VbP7?fK#|@BcI(cllXb^LU!w(?q^lZt02SZ+y(W{7~C!
ztsPS*Hc!=Eeb6Ip^|?E**L^v5?N;vXP38OYr)GaSb^X~!?N2u6-#D+XS-jeJ`JBl;
z4~p;ZyZ*!`W-b5X)WlGY=Pi?%roV}gZrRJ;$-PPS_ig9HcS`E=-b8i2R1u&4sQKuk
zradQ8%=R38|NleY%8Zpe%JrL8@tEGdubDTs^p3-mopX4W|NrLxkDqa><~>OKCM2Xa
zBuKKku?cKo{3LO(MvRSB;Ve^&!oEZQ{|kKj@4Ui`&CQHWLReiv#r_H-Lu4Y`Ww5?9
zhUWigdE6YjCNlCeFldA_?gX2cl8~5~kdjj3q3z|Pck;~n%f6mxPoF<~##`s4zSl_|
zy%U~mbo5r9@zgzc!bjWlr2cthV*`T=x>{#Fbx)q~Id{%mSI>;4%fP_M(7-6;|AX`&
zif4Z~x4mrF`OMEbO-}Auo<hRZ#4AsxCng9?NW3CACGo+8L>|GU1h&k8WRKK{M|0u>
zBB#up7r1D_#5F5{2|Q!jgV_<29==}uAR=bY{5dl@io{#rExuCiaNxv&BPUiYxwH3s
z_1m~(d#mg7Zl2x~{$~1-@9qCpZhZR}r~hm3x98LDZ+%l!vp>sam+S7z9SjGo%Iz6o
z;g-VC<Xx7(<KUNvva=IR7<^xFurt8()q|vp11pXT97s4^)!t}Qz~q%Jw6IKynK@ae
zPW2HhhuLTDH)#eEj)q3kyvAKd27mT)rQ9|sFz8^+6jeCOVb&<f6IiL`aOSYW5ru;a
j?UyqJrYbpzY9)xt--zkZ=y7Xeh^gk0V}1cF0e~0)P}lDc
new file mode 100644
index 0000000000000000000000000000000000000000..c5ba7a6a78edfa3d4e2a240c87c4092b843379ae
GIT binary patch
literal 1232
zc$_QucXRU(3GruOV7b7+%>V?Pe;61UumWc{Hw6X;<`f|FDgy(f{_@YVEpBchzCgV;
zKru-mmMK3LTj1*$;sF$k0P=Hz>LQrf6g2&V^^HJmpqL#HC-8F#d`QksEC8A(0_2+m
z<(wFJnU7_pCIZc8<N%6k0kIj&(oLQjKtZ6G36L)h#41ewtoJi=ODceNoB;AcYB|_1
z04+<b0Gh`b0OYfQF;f6TUSe)4P;3SeR{_;$U&;S*s35<%1Zb}hP>mQ6EBtR`Fs_)B
zoRE@`z>p-nHnsoJ&)j`R1;+asx(s5h_9P1%@v_SB_$Y8Octmm4F)%>Y{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awi#J85IyAp?Q7|0)evmc9xr{Tw8!#d?!N
z@nG8oiB%WYo^eqW;O+<%Vr$#yD8In};_(;4Ut)KC>o5EsmZh2$`LAZ*@Aox)4c>bW
z9)7vo^~sIqBkjrZ<@Y8(W|&z2b_e6j{nmMpw00EUY2t}{@zPXpNA-q4)9Q!?1#cc-
z%(mkD@XFRYFX5rSthwSJ|KqwkP1QaL8;(!ZlFCk5p>V;iiSN3cc9_J3w2sP2g}pnZ
z7e6uha_QQ-la>aCzmpeUk5s+o>HcMjR7Ye?>Sm9g)0b4N)@@mMyK86CvAW}}5lT<9
zHY`@%-qmK8ed*rKUu%}gDZXXTc^MzRc;%*tnOUy`&#|vo@_QBX(CF-0zvqRiU8Q@}
zzb>7USa@Su=(5Sa^QJ9b#9AMFp#QU9F0<C|Yd?c-f6+5#ahvo1?y6w1R>i%d&%ZY(
zXMM^#^(y${YcE5g{kfXXR}4ZD{|e7}+I?O#FyK+hgbhZg%KlvuKXZA}l0_4ON?&u&
zeXP5Wq1)=IHze*764DwHB-z~91U4{!k~ml+#>T2}mZ?Qy-=Y8i1wQ?EUg5>&X2vEV
ztgfJ9e}$1DGLh{vSYH}L^Z&CvZVp`&8F?8PG(s77g3U`wNK8yfNip%!_VUp?dFK3O
zU(d6r&!0Wxt#eY}%U_3KlMfFMPf52|ageu>(d5magSxwU8<lu^e0X|zZrT5ktn)Db
zHzDzt;^{xmO-tL`H%fCvq)PnzaN59t<AlK<@hOQ9CM1T4Cnd0D1|)MNXB3~)U!il#
z=e$PX1&?bX21W)Z!5@5ePJZ|rSfHbK&i|Ybi)Bj)`@}bl3c!S%loAs3?%wacW%`fr
z?X}<jCiR~7H}6Nk57!IjRM)Q;{~Pza`1JdzGP`~8rb>4g-L;8fh-66l$N&qc6ow}6
zviuzfzdV$konXS?`+|d=0iH)5Bvl+(aa`a)!r`j+Mw0?2uWX@(Wm3${$ub|^jQ0cO
z;~O&BngvCaH(xYZtigQv{%VJ9yv*#)5);B0FZCn{OB@uKx}52V4UZF#5KocCT;@Pc
erjUh9X&)QUwssuKP~l)Gc+KMmw2%1(1Oot;VAGBO
new file mode 100644
index 0000000000000000000000000000000000000000..2c2b3d81b1e99dde81af6760d2b17d037145bc55
GIT binary patch
literal 1240
zc$_QucXRU(3GruOV7bA-%>V>k1`Lc0Sb?*fn*sv^a}JPslYxQJ!hDx}yqjByFHo-s
zP)rhtWy+7m7Wg`b0QoE_Kt50{V+0eMjH7?Bz7dEG6te^31b!}o56QWS1wiv;fP8bH
zoD%~t^RbN7M4&!K4xpG85Sy_q-Q<}86a<Qy0Qu5Dtit5adOst#qylKi1t1@!mV+J0
z%1x{Qn#ULb<g<Y>QvgF=Vs0u>Yz7dw0M*ZUB<0*)kY8K^v^NB(Mhu7*{<kq0SIkLH
zNJ&Uwc+@Yar1bHRS-j+qnehy+5_+=tHge8vW8z~|6JTX94dDWY4OH!)LqA#-yMD4V
zvltjHU{Yg<vS9`}5okRVL``DdL5s%5sf~>*yEFNjnVtDt(rOMQoH)TElA7=|rGe3n
zt>nUi1wi<t^&_{lNSo9G9%f|*O*u{>kp8oi{Jjo4h#cFmvh~5T9p%Q-3%OR#WaHA^
zq0y?h<ZAB@{>)1}xyGuJnz5BE`40qRswDIunEo-EE9c;7=se^8ncw&3R~j=|RW!FB
zE^nNf%x`EX^Lxi9u|DQIzpwX2cl`A~u5Q7dC$n9U{e4K)+TeA0Yr_xA6@~QG+EmCM
zXN=n`eq8s-zs-+@J09FAl&&mj=QP=N!?^2^5GxPME|0>KJNr3=m@mn2>asqU3{Soy
zGfOM=yXa09wW{6guWT|{G&`=VPK7;b-i#zW;m5nKt@P@-G{sa@{fq6d)(C~iYc@Xg
z%T&(F%6b+YB7MuWY|D`~%Ow@p`bS(o9pT$0x_0JTFDu?pTAs689x7>nb(`9GF*J6?
z9i1=hdKPPNtzKT5#x-@_sWt0V%Rg98ty$i%GPiSM@lo!RZ%n^PxFsC<pFgYogsS0^
zs89BGEIy@~%XUSjExO-*#N*F`lV2ts4rn|3`^WwMhZ6&5ooPSS(yQ_|R;H@u_?lIc
zAubc&n5zH2e*7oXW8crbpg2rONNY%tWOHK^*ueNn;$V#!8>_-urWS>LhyMQ;`1Id-
zg%_Kf8JmQ#x`K-R6-I{0M7GP|xKCqf{(qLo&7o@|BQFDkMkwP>aGFR-NK8yfNip%%
z_VUp?dFK3OU(d6r&!0Wxt#eXe>)Z(+ZO@bX=Z(z_3@+$uo%Pf`dBW%1Id5G(GnOs`
z10zENqm2I#(tjwP{o&m9vR&shKj$<#xnp?>2~!hOo=#6p5SWnoL|{tdg9(Wy0!ayM
znE}ZiDK84o>95c^<#S#m@Pfy+5CbCvli&}&IwwDT4J^>nJLiAShh=9gkNL#Myb8cX
zos<$1^zPp8y=D53@9nkU{wDRF_BZcGzYo_7<y6<N7yldgyZH3`s4}~K@uo_57u~gq
zVR+4zuF3#O?+FPh3{Bo;`8y7Nc_=$O!Gyv01qV9=Jik0hsyMLXxWIvg!&U8#CIw7h
z*+L7;q?nnLW%4rL$*!n*CVt~=f<aSKikVazFo*o87Co^o@j-$B^Cc~ZS%NuC$`Xq{
tg*41)ckFO%a^ze7SwYLSK`Z2dj@^cx0)Y~}ER22Ad7_wK0CP4F0{|iX%}xLS
new file mode 100644
index 0000000000000000000000000000000000000000..a899cc9d2c8357a879176be5507cd5a6696d454c
GIT binary patch
literal 1256
zc$_QucXRU(3GruOV0pp7%>V>kTNoG_umWc{Hw6X;<`N+D0Rsc0Z<d6wk(*nHFHmm?
zP)rhtWy+7m7Wg`bcmTx;fc#vbx(FsV{geK|`bHo&P|Oa96Zp9VJ|yQR0@X090Qu%X
zIVT2Q=3^PDi77xf2T)84h|O4*Zt~0k3IfGUfP85nR$=mIy`Pa=QUSCB=ne*uS`PLL
zK+6&<faWm<0QqcS%oM<omzbLh6q^CWoeT`DjICFLP8Z}C1O31p15_gh#0vl07>q0C
zBqyXKBrqg}U;M-Q@sC-&<c^u~46YJ-viCM}&TM1iV^b4gWiWip`ICVGs`k&JAFYaA
zKUtYs42%{qsWC*^FoT>3w4Mo~Cb90IMPuXC#>SQ1nf%Pm&ipNDH3t$-oL~`2O?aBp
z!05(Sa^b)NApFt#k=t3MO=<xTvoeFG9H$US|J9(4euoVNYBRZnxRX-0v`lz0L2I2r
zQy#}k=B(@Q6lWY!>Q%li`-%0;A133Z^oP|yc;aF{GxI1US=Q~VF8`h*-{8DQ@!&h_
zqehQdifi0#ntbLd`E8U773??kKd*f6aNsYQk9N-<CmT)v$XLEzG3x-QP0kVJJF4n}
z%PV~EDa#%1w^O>;?BRRl_lK9<c?lO5ri=A*nSSP+v+zjEYaUA#y^b)A7b|z_?cCk=
z*dXO)!u2D%1<AYay;}9{(V96Ok$U<^R(<%IrMYgd@lw4#W=m{0gdM!`en(GnH*d&>
z>RUSRUKU@udf}3A<v(d7t#{T%rpbCM^CTpH-hLn}l9g7sB%HO^sOUpNcag^Yt+#tO
zwVHp6+r3V+>iKWCP}l7j-=@xcKJ%{Gsq4FHa;6qf68qD(M}9?A%jwUFTi2{P)a*Oc
zcf-Xa^J6}*+jK<q=A_TN8#frNjhBelG@Sh`Nw4mo49~RinP=L*{c7L4h3`_#6m2Ib
z$++C|`UvgUGoqjDoWpngZ+-e7rrUBt>lq+%nUIjykRZwC#wM_V@sq^C8ZkCjg|kd8
z3i}TI|1a?Azw-(&Ha9ai31M{w75giU43UX!m%;kd7@Gf|<#BW9n#jn@z@QPzxD#w%
zN<v~{LQ2XOKW#4`y_09oUp}jS)>qd{=cK;ZNgcfto@;dUR-W<n_18IZ_RMAj1A_~?
zT4z0VPoD5Ocg|Z^&y1zZz`)4Rz$oMYgY+MYXMZ@iy=>R{%+EPZPVQKqLc-L<DUYTn
zCJ0POoFY0U@xg?|649gtw#<NJkAxXd=fnp@PMJ9`aM6N^YgPi&amKO-vm+)we7*QV
zM9iG|b7pcB3H|h2d?482z=;D#POMmRXYcpww{gezR@dj<JiRCU&GaMR+yATF`1UVO
z|JUAc&!^qr`lhC4f0oNG*WHyn7&JTz)<VK7g`vs2EPuzrFArsBCzvq!zTjYIfajeD
zNfifH92Yo{aJZ_y(WHRMD_dw`nG`c~vP|KzQ`SHa?>~^o)+{KZy!oQRVh!fQ_g6b?
z<7H-VmY5L6c&R5rSmL0-)a6V^Y<Qe_gm{W9=CTB8GKDN;O8eM&wzcC>h6)FRl`{7?
Kpnc3QAQ%AXo#oj8
new file mode 100644
index 0000000000000000000000000000000000000000..d3fa259f9a8219187920e40ee33e8332118a789e
GIT binary patch
literal 1264
zc$_QucXRU(3GruOVEMqn%>V>k2N)O`umWc{Hw6X;<_aM5Ap-+rz`vqbrfzN_zCgV(
zKru-mmMK3LTj1*$;sF!`smlebi(q1ti1ZKEHv+MNVs=2Bz|SS{Avrg(0BD{DkZ%r@
zb7J6SK9-T1m;z*T0L8R`*o<ZACeI9@AW+N%$d?9U6()by`x&_<K=WB%0Qn%b9PAf>
zmL*mcfZPS-vw<;F07G74ZYof01`zi$Ft7^jc-4NqAiuZ-Xm0{gjTjIs{BL70u9%aY
zkdlzV@F;iLew9Z*bN3k)81HB3GKjI-lPqk+%PPa;qrk=BKb>nX0|Qj;pF=-d6}x`2
zGP4*MEnre(h_Yb@IT2_*6GTm7-9d}S#;J{sE4wrKnVFsWTheL{B%C<GB9fZ$G^K&j
zjjiOufdxSLqxB=Vvq+oN0v={%22D9mA&~y7L5_Z}4Fqa4xrC&T2<~A#s1!PfRj8y<
zAV_@0-9uJhcaJGunfNxO>!0Y3L;Ic9f0%x!HTc7$UE)7H6p}3K=2aTk%`<EWelsEA
z|J-D`!X`~SCAozj_muoT+87D;8~VOivO5&{%luKY&vEe-wa3Em<eaZG2<Id!%O%c~
zvCu3#xxeH1jlO%zc1<4tU21xAcO7QtiTZM8(MC63{zmVR9gm_E+K-rCWKy*?y>40l
zy+Ec-NA}IE3TB%b+qPH5Ru@Lbb}CK3o)q}2%253KxtU(m<D@Tz*{n`^qp0`z%?B>g
zH}lN8RcrfpNkvsDov(LpTg-NrXP#j01@oRB!PpPKAFTgyXpYv3DHEHg>aKQl*!D7R
zs_x#6-fwR$do%aF^wX@ppJJn*RL;A(E^yzqtEb&8)iV!%*Z3o!xH-T{KI}BxstemV
zFLT~0`scIpd%E|X3A&d2{G7&{KN=@r_Y#>LbEiw+r1jz_waGU>oU41jxQgplOo)z?
zn`G7Y^@{VRl=|p>it%fc{(maFj%l)>gc<`RJ`)ns8WJSg+}H#*Fn*FaSR=;9s&JO6
zMPc8e|NjL({dZpB#pY(lCLye@pkjZ8ks&gX?J`(j8bkB{vpjANT@x9385lG|8Fzxs
zOG!vfOh`%D;;HTBqj&Pm`O9au&-&_m>73N}I;o>~!gGy|-pVtcy5~;#Xlv>k7#SE`
z(A7HYseAH-&$)Bnx_V|TT?Ph5h6Y9%{~x6PP(1s?x$R}U&S!qkX>xMM@)Q!LCQf-e
zJuyLGLSl%(l*9)U62Ax}C9q`%BzvUHcswUQAacsgd4Y=-OkA@Pn3yw`J(wLa>EY|e
z4<cgb%%3xpqez%FZ}E|6hXW@L967OK$(_C5tKY^Q+gn|qck}d~@Hf+sd~g4+a^u^-
zIQ?IHzdfIJf9so?n*CWWyIgly?qImcS7ZwhvlNCV@3Q<I2fsX&ot<F9;QNAuodKSQ
z9wb#9SaDq7K*Hgw_C}KeCa-Lvg=JFA%*irkn%}r*B%RUUF#BKv%h3}#CTBEG9r*BF
zYeMef0|yn@mV`E#DBoalGjMsjlEIL_S)iGvS;p_Wb4V9M=!%BOnuKD-MFvuwObiP@
M^W*{TV}1d_00V~NBLDyZ
new file mode 100644
index 0000000000000000000000000000000000000000..cceffbb5fa7c242e0ea371a9cde8d5420a8b35b7
GIT binary patch
literal 1220
zc$_QucXRU(3GruOU^&9T%>V?PcNiEMumWc{Hw6X;<`^LJDgy(fW`U8Gw3}OqFHo-u
zP)rhtWy+7m7Wg`bcmTx$fczYwx(FsV<Bk5o`bHo&P|Oa96Zp9VJ|yQR768rT0rJg(
za!w4q%*QfP6H|a}4xpG85Sy_q-Q<}86a<Qa?2rax6()by`x&_<6+k=o0Qn%b9PAeu
z7;+OUfaWoR>}3OErT~V##N1S%*bE>p2CDbHt}$1<AiuZ-Xs-)UjTjIs{BL70u9%aY
zkdlzV@F??I(ey_@bN3k)81HB3GKjI-lPqk+%PPa;qrk=B63hj32~_Q$LqA#-yMD4V
zvltjHU{Yg<vS9`}5okRVL``DdL5s%5sf~>*yEFNjnVtDt(rOMQoH)TElA7=|rGe3n
zt>nUi1wi<t^&_{lNSo9G9%f|*O*u{>kp8n1_vRfk5NY)n%DB38)i0K~2?+%v#tH}9
zCP--L$j*pZq#?ll>GBbtJ?`@tzgql-_m{@o&O+<$LE3_+KR$o`ySRq;ftdb>f`3&J
zR^J33o!!_!@B0Ib1BdpP-En-e-|F2Xy$>?+iac6XRdc1^iJ7fR<(IzFS9{A|hP~q3
zJel$hALV7s75|hU55K|kea1qUR^iPVZzddcSkTYHyw2GrKw@H8N9Cl#*dlY+!-1E)
zqj#%`A6T=-Z|l8Ow=a_$CET)9!Zw}^^ES+#Wpr`lMb*38)`%K^)AlQpo2b0C%sJXu
z)iCtw7Y&1DU1c7(`YkT34_!1f@%-g0S%K%+Z(I4@GTLPEEK_#&rh7+2*L4?)xNfqO
z3!3~gC4PzT;*5jw)BYtM-MM$m#_VX_t-X9R9TN`yod4D`JfLUdpVRNzkL}SpGv`#W
zA&;`5;Qnl7<|`3eH|>75?tOo2TJ9W?#K{`B-xk|%@O$0%(?w+EhP|g!U;bxtlf1_Q
zj<bY>w1xypHa9kb4UC^84%UdVu_~NpYEjsC=>LC#Pyd})c(J*eu}KK4E2!9CVPuF*
zWV;O3m&VZi|16K2L)Sz`UIqq@P{y6$G?0>zn3#}~a>ZZU%SZ3zne&&=YM=Gh_0l=1
z@8z#^;_R8tCI$u<bhXZU>YhB|bMBnCuAUi7mw|zip@C7x{|D(m6wm%}ZhP6T^O>J>
znw;FRJcWd*i9e3%A3mU<aQH)TKwG18TVP;EqvR6DUd5J$CoT6c*rZ`>yV%81IWqX*
zi31mW4w`RNIegvy;Km&m_LjziPk1iPabEPev0-9k=fptIoz?HZ-`d+#{r%saP17sa
z-_Y-T&;Q?Z!`r`m<GxhCJ+Hez`{vIdbyqt|y30NnFfu%;O`6NV01c-Uh9>W_{2d3s
zJd~ZCV8Y=0f`gp_o;MyORUBAxT;M>$;i~pVlL98MY@vl^Qq0WBGKs0Dqy%RDWGyq6
z=vg2!v$svsRZ`+#9P6fB2@8oM4O_S-q_NCCz|pqCMpWThqCnDwgb9aV>U4MsF5nV1
Z=;qJSa}jY<c4W}8=YGWe0+@?|7yyMv&Wiv5
new file mode 100644
index 0000000000000000000000000000000000000000..20bd5f7d5e3ca372859008c1b02156a65db8a7f5
GIT binary patch
literal 1232
zc$_QucXRU(3GruOV7b7+%>V?Pe;61UumWc{Hw6X;<`f|F8Uq8P{!um_Yd5zLU!Yza
zpqL~O%ak9BE%0>=@c@cN0QtE<brDQ#){Xwb`bHo&P|Oa96Zp9VJ|yQR768o?0rJg(
za!w4q%*QfP6M^P4asb7&fY^*>=_bz%pde7p1jv^LVihKT*83T`B^5wBP5}8JwH)jh
zfR-gz0L^0z0P@+um??lEFEKY2C^iF#D}m~>|E7JGF32w~0ov;WR3iq&3jf;}j4S3O
zC!{1KFg#*S`rPv9XYM|u0^|J*T?R2$dy<8Xcv)q5d=$7COyjtKCPUT!IrO7dvFj%*
zGmC-I0wy(vC>v&w6M@z<LDVGH9kgg{oZ8s9vOANXnc11YC9UQ_!if_sBB=>aQyLiE
z*h(%OSOA1ST0e3-i?m5C;9*u~(3Im80_i_H$=2_%fynXyp0~VoI(Ro$I&Se;`G#-m
zt+lJJFfPq0$rG7#>{Yw=ZAq&FX1)WO7HsMd^w#Xz@s3Z(qc`>E^XLD6e<)^%c_b^?
zcVBnY0YM8p=HolM=P{|Dc=Y;!?uQ@B1=8UseIM{ueDXTAqSSFW`}?Z}Evh~bB>Drb
z3!1jSP%Yp-{ll_ATIV0vor6X@kBdlE%V=~R;u4jS*rjn{ajAnSH&5rY6EBx6GF^O^
zWm=os)K6uudzhD1)f;4mT3*ww$~ka!!r9{Rv&Lo{!^E}9y(X_&5@Y#u-k+dZrrRdm
zdv_vDqsn!;>f5YUeit6pEh!00c8+>3%6jdIZmq34uh(R8-&G~y`AbjEHQN@nboF<q
zf^)&a`Ab%ZY`o{0EzSLJSA+cXxj)$~_Df~$|Kj^Tn5WI~?Ek#0hTR7`t~5P+dC>5(
zwfW^V)td{>-<<Jx+tMdWZYw=z&wrp3U-NMC?Erz#i7oH{Y}AkNNY+$(9P?#1_uR+2
z`xtjQKiSU!iMxb^w1xypHa9kb4UC^84%UdVu_~NpYEjsC=>LC#Pyd})c(J*eu}KK4
zE2!9CVPuF*WV;O3m&VZi|16K2L)Sz`UIqq@P{y5L^HLHL6BANWOnkJxeDqG9Ie+=A
z_E}$DFP)S6TIWvqXlv>kn3xzCT+r1z>#2M4gwMHi-nx2hodyO*h6Y9%{~x6PP(1s?
zx$R}U&S!qkX>xMM@)Q!LCjL0AfB1lc!r>3`0d0-WZGo{Jjgm_odlh>Yp0(V+V3UTi
z?P3>4<;dWJCk|ZjIcUC7<?wa)gBy2P*jpM4J`o6#b=kz;&@i#Fb7G+9&g%EyZ|&`=
z{{HXIrs<XIZ|HZv=l}1y;qBkOabK$6p4Z)<ee>s!x~m-}-DRH(7#Um&QjWvIDTSfQ
zyDWdl!7mSGXD662_`cv^XMpFC2T2tNRvZ^NkZ`!Fz0stA$tzoEVVM*&bF$2`4cGYJ
zoSGS*VBUO?qif>Lj2VGb8Xvw7Rk+>O*zCyTv6>;%Z3AadLdU6K#w0m5MK(@0%Q@@X
gSBNmK3Siv)<G?c~mqZgG7KX<+d4NI8!2AM&0ps`7@&Et;
new file mode 100644
index 0000000000000000000000000000000000000000..8ac98283483717271605853db852cbef8c372e40
GIT binary patch
literal 1164
zc$_QucXRU(3GruOVCiAtW&i??1_nk3tiajLO@V=d*#gKs2*e+vxp&36xrO)w#r^>K
zl0Ym|ek``Y*D=HcXr2U+pUJ?$7{SD5ywN{c-w4D8irE2i0za3)hveME0tN=A7eKx_
zP|k^gm-$#mYGMkI%>fkC0%9|krJFo6fPz3V6Cht2h*g;US?_1$mQ(=kXaPAGgxN1J
zFytmy0L^0z0P@+um??lEFEKY2C^iF#fqrB*P!F0SUXWj00%QaI!XO633jf;}j4S3O
zC!{1KFeI@Gdv2_#o4uar&Kdm%C7$Vg?{BpDI5V;{3wJOv@Ro2YGcZ8a{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awkr8n8R-uz`qek=%u)Q`Rk9u&^~{!$zYW
zD$yJ%8YgGnl~5Ps44W`lF+SwQh4U}+zp(#em%Wu7dDBa?)uW?&-8;+gtPiBt3uK&U
zo+ii5^WjlpLv&5Wor8-F<jM^N-phS25WZ7+=eWX+37WgYzs#Gn>L3&6n&XV`K0YgC
zSKVjxPD${*+;@ee%a7NpIJQ{w@L3pk9}{EE^5B`6=wj{0bCgR+dr6PT7Vj$KkUecW
zZ~vJVa-KQ+Fy-sE6;BPMPZq8b+UfN=Y3r2DtBhv)W#@X_-Vo;XHd6eOeNz9O&<j0p
ze$3u2)g`+{TzYnzZ_@?2@0GDHa>Ad5E=`jAqkGu@P;2(WvTH?*JEg6aVv|mN-=7w}
z_E1aB(egMIv1Mz-kL49x_9a<P-OqD$#jF;soNpB~m##gz{mFL8rNJ8J)z-|{cCOW!
zR=8%-vc=t7KJr$`OkW3%kA#G@h6G7AH#UI{jGrV9)`+pODx76%QP_9r|9^o`|D9KO
zvALPCNeHVesMud&WQa^;yA0Ns#?bu#ERUN**F;8M1_q5##+~5!O-V>hOh`$Y;;HTB
zqj&Pm`OCUmXFYXKp71$$&RbW{%!Gl7%}otx+gtfRJoZZ@>rWW`osjxZ@!+Myhi~w*
ztTE!L{+TKv!IC1u$8pM_;Do^*fusbs%z)&Al#ar4`YUu!`JC4Xyx?&y#K6eFB>01`
z&dCp70}FKY&iSA7VY%6PJg?>)6O$X8nwy*I(|3FCXMZ<(clUSk>9}WmllH6Zv;U)h
zaNGCqRTtlHwcq|{UF!44zeANT>E1efn3drQmoG4bLc=75p~<@}f5*Wu4`pX3m@xRh
z;9zHfr|}0#6$e%v7dVh`xT?L;q=3mQTWDdK6f<+O%tyD?+zIB*2RXVX-prU0IHmF7
z`%s13ZH>*2JRYkVGTk<C_9S$i3T8}_V^d_~WV4)eu5pD3<EjA0%|8x2b8<;E5n^G;
OoyPSTXczMf2nGNm6R7O~
new file mode 100644
index 0000000000000000000000000000000000000000..fa2e6f3aa6dbcd1ecfa65126a69e92a02420e4e7
GIT binary patch
literal 1144
zc$_QucXRU(3GruOV5wl>W&i>X9tK7RtiajLO@V=dSqI464aB!)?VTOm+(LYTVlRMv
zNg$RfKh_=N>loqzG>-+y&tza=j9_9@Kkgr_Zv<ik#q5AMfuBp@Lvn6n0Rscm9U$Kv
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ6G36L)h#41ewtoJi=ODceN6o4EI!t56q
z7;+OUfPBUPAfFA4nF1K{5_405Vl#l)1!&iWC7t$+1^LA#KsSp3)rbMH!v8h~<BB=S
z2`LE)43AnrDouX;Gk2d+f$@HZE`u1WnmSe^URD_%9|bN3ryE>Amq6A2IrO7dvFj%*
zGmC-I0wy(vC>v&w6M@z<LDVGH9kgg{oZ8s9vOANXnc11YC9UQ_!if_sBB=>aQyLiE
z*h(%OSOA1ST0e3-i?m5C;9*u~(3Im80_k5mVXqgnqe$!dC;U1h-BZLab9in{RC9F-
zl4R0U$qe4D<6Xv)^5~q%$;~f}JM20iFyHv0G;gk~u8YeFzQcc?z1z8;LGJ@g<FDPv
zj5axl_fM3*vd1mYsdM%T-IAluPt5d=MnB=()myCP`nqTRZGq5@4*aQ4*w%dz+SzHn
z$31VM`a9+Aibso|@cosaaX6`8d9y@LzyW8Y^ftS<0$ftcm$)?li)``UmsVgiO-uMs
z+aBg;W%W8|mwnhY{p-_!mlI=_?u*cUdd710%q!B~Gm~9T-Ov@f#k^S7`1$>>$Ja#3
zTFg(otzs(?{v`C)#-K0%BtHf3;h6g~!Mp3T!PSPKPR;lA)hn+`3BNLVe?4u(2J8JB
zo~lhh+&C%tXY~1!*Pe?eXDO@q?_ahh#ba-=<Ps~Nknf$PaaQjlwU)Anehk*GV=_>z
z;{e4$LPA<Yf+U+8o4^LfPZ9@f#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~
zL?*Ib2FG0*L-YT$JZ=tM6B&6K7&Jl|cY@<IB_S~}AtgmbSI^Vu+%?}dCv?tvob@@c
zVQ6k(kdTm)l#p`a-@)@AoM(M(=ef)u`Al9&$JVCjuEPPX!yB67+Zr7m+7h<{qq&{u
zaH=9(+5|RJHf=Wh=YN_`DNS(Xk~%qy%Z;rqjcZ@0s^mU9uD+v3f1aG#sXFs#<qe&q
zuLKl;$u21+B<S6}-+RmSAK%++zx_?>J?(GakA5Gn7s{!wUoZYQ?sxI&_fchb`{GTN
z?k>7(6T@IL(`79KBrH-Gn!L;McO3lk@UwP;34`AZ4n+oddVY{pabU%9fddJLtJ)cP
zmMBYfFsm=vqBikWC!?E#4X@YX8wL$}c`dTWT}B3f_Hw1%HYhOYV9XR%ILl$yD9967
sspW9yu)-0Ag9`1JLj|TPIf!Z{h{@lG>CotLYhti#<$l8eDJei007);ODgXcg
new file mode 100644
index 0000000000000000000000000000000000000000..2addfc4b29dd49e5e1d500fd031b752365f34300
GIT binary patch
literal 1204
zc$_QucXRU(3GruOVA;aJ%>V?P4h)P8Sb?*fn*sv^a|Doij)8%Z?_7oCR5!N}U!YzM
zpqL~O%akAMj`4L2@c@ds0Qu=abrDQ#^56Y~^^HJmpqL#HC-8F#d`QksEC8Cv0pyzl
z<(wFJnU7_pCZ+({ATcc<He*@(z%&CW2oy5`@}+@Tg~^}wenxId1<;N)Kt4z<2m1vE
zhTOyopm~e|Kt3B7GX*f@CFZ6A#by9;KG3f7i+9`T7UUO~0ND;eHDW-l@V|}0xMEIn
zLP|mcLsI*>OHV55X0PYDb4I^GiDx?B{P;#6XGV5r;SMGS#|*A$1_r3wKZky_Dt7&3
zWo9ujTEL{n5M{#*aw5=rCWxBEx`P&tjZ+&NS9WLeGc!B$x1`k^NH}qVMI<%hX-Wg5
z8(YbR0}FugN9#v!XOT9k1w72k44QJBLLmJ|C;4YF8A=?z-xHM~zSKNJOtfU74-0Fo
z$dN}*C9mt|NX|WIKhe{HZPzQus+MnTvELQ;x|-Kc_PnmU<w1+$ewFvtKR>B5toz8s
zfA#v2I~(2Wir+lGw!1jy@Qv#7828KZR(TJO)tA*A{OPZ?_m`@>v2^Gr_j9tpcN}kj
zEH!Tr`|;8<f3%PDa{Ryek@b1JVdEh|)>xyQ35OgOv~uKrvt^keF+q%D=cG;Zi%r_n
zf<scv?3CpjI<3Siw|VwvxaMBue&V3xG+FOi(wePZn=Yhu8J+Ezura!1L(-DO;2X-z
z`qT8R*NKINP7n?{HP!UEVX6ANE9r5m&1Q*P-maNqm?fY7YX>)XZddH0%Q?A8rB+{>
z8#dX$`?UD~m3jAX*rkgKNAK24W_A1X|NW7v^N&pMoAm!XZ{jMxv{j#cm8$MK^c-2Q
z<vdrWEM@tgRmFEsPtvojUG-f#XU&$<)#tiWCuQ%G6lB>H{j9o8Wj_Po{1Xb`_)17f
zYe<k}b7K?O!1zhxV2v0XtHN2P7KMF>{{I*F^xt`f7n_?In}o2sf{OhWMux~lw##6B
zX$;N(&+@oAbWLRBWnj<<W!wpl|CEHp#DtWT5N$nApL5rIPw8u(Jnwr_NAHB^8Xdir
zXFPS!o$%52JgI-)*wnxvAt5CxA?3oqgXcdu&-&QTbD2N#nY@sWtxeBehXY!N3uec+
zH99)9J)8$jBkerOvjv!qJ({JO&7B*I>l{-9)2iG$o7^@nYns@&aAs3}l4slc{-)%@
z!n&fgB+s-uxxjS_=T<r#IC0>}i4{xk?EPN-HtyKo>iWE!r}u=vnSSJZ`+t=i-~Pqv
z|JwWQ`Lz36-_+FX&vM!2y1Q}*!=oor8yTSClfuyCU6#M&;FpJ=wG&Jj{AO?{GQji0
zgQSWBD~<~sNH|>8-e^+5<dyv>T<wly(@Zx98(y!&Hw+r|@>*n#yNnF}?Bz<iZBSs)
z!I&wkaF)ZYQIIFFQp@4YVTB_K2Nl{chYCzpauC%@5R<<V)1lGh*2Lgo$|J(S{DJ|D
E0rgzQK>z>%
new file mode 100644
index 0000000000000000000000000000000000000000..246154e2eab0b2ce5bb36867ee858e0e4bc519e7
GIT binary patch
literal 1172
zc$_QucXRU(3GruOV41?e%>V=(84QdJSb?*fn*sv^vkQ=U2#8;Oh<v@&%`L<isFwrC
zmjq&&@?+gGzK$UtKrs~{Kb?VrF@lLr`M!U!z7dEG6te^31b!}o56QWS1q=*KUx0jb
zpqvu}FY~dC)Wj4Z8)Tjq5Sy_qePEga6a<Qy0Qu5Dtit5adOst#qylJ156HnF%zlA^
zAvduCXdYt#kk1CjOaTmeiMgpju^B)d33R*kj5Q3q3-XIgfNUM08ZjVN_}|80Trnp(
zAtfP!A!(udy6KO9=I%2pFy7D5We{UkQ^#t=%PPa;qrk-wyqW6+0|Qj;pF=-d6}x`2
zGP4*MEnre(h_Yb@IT2_*6GTm7-9d}S#;J{sE4wrKnVFsWTheL{B%C<GB9fZ$G^K&j
zjjiOufdxSLqxB=Vvq+oN0v={%22D9mA&~yH6ZdBwHV`;wZ&|;<TchMuh_3F*2MLl{
z9@_*?1k71E;cnr-8@!!Ox*U}=`(%If)VA5Vw%a+i*G;>+wpA=i@r(7{+UL848~EZB
z0_x2>WVuT|zPWK&^q#DFfx6Lg-;$%VAB*Yj?R#vt>mIYR>*uCxdqYEY1Qr#%c__BN
zPrBmB^2cgUAD`@C)wGk@E?9MahQs3q;muEOC^R>^@N53Eab^kV@Gw^Sl46q_l{_V8
zR^ZOP=TBBAEaG3TbU(%I+ogRx%%UeFlQu=_98X<&*~V~ZT7;*o(_1k+u@EV}UfK7?
zX;GJ>F4-9+ANe27z0~v4+Wi-fy@;LpG<)-v<Fz6zC4UVM_upE%_V$g<3E#DEe{T3F
zr2OuGc+_i=wh)bX?>UoJeNS7}eb%e3*`e>rdfAK1+xC50{c&z}SaDfIK=l6UTRxup
z7Ja?%=PHe#Zblqe7OZ-DhP8&#u5(H=10-$|64DwHB-z~91U4{!k~ml+#>T2}mZ?Qy
z-=Y8i1wQ?EUg5>&X2vEVtgfJ9e}$1DGLh{vSYH}L^Z&CvZVp`&8F?8PG(s77g3U`w
zNK8yfNtvRh=jn6qn(rxn&6DSK&z<nm_B^S7-q_f{AR!?oDIw*;zk}yLIM4dn&U2YR
z@|nDlj;&44U55i&hd0cPZ)<dPXiJ<9Oatvahm{rC(k8H(vQ@WnJpa>lYSEc5JwmL#
zDZZ@A%+AKF_m52CiI-<R{^-%4CufdKI`hY7j@jcfAprv;14AP%&GPqu<91Ike1C6#
z^f#mTqQ6x?{(WfAxoz+M>)mzh|L%NRu4^rSf4$Viyo-5qdJHyuycfa3B!!{LyDWdl
z!7mR#YbTg6_|4!@WPqpl2T2tNRvZ^NkZ`!Fz0stA$t(L&m{Od>qM2?EHoRVkZx}S_
z<+aEfcNrP{*~^u3+n~UpgE3Q7;Vg$)qaaUUrIy2)!wN?f4l1-?4i%WH<RGe*ASQn!
TrbDC0t%*Uukq4L~UqCPbbVjqc
new file mode 100644
index 0000000000000000000000000000000000000000..02dd5153115a78ee427ef0fb69e565d48f6bc187
GIT binary patch
literal 1176
zc$_QucXRU(3GruOV41<d%>V=(a~K#IumWc{Hw6X;W)~pyC=h?X_MGpgn_Gx4P%jUV
zFA2ml<;S{Xd>unPfMOazemVmKV+0eM48MP{z7dEG6te^31b!}o56QWS1q=*KUx0jb
zpqvu}FY~dC)Wj4Zn*%7O1;l17OCOkK00n_!CP2P45UVixv)<3hEvW$7F#+UY5N5x?
zz>u3*0W^;>0LW(pW2OLxyu{p8px6u`js&{h!z6QtK|y|T36QM=R3iq&3jf;}j4S3O
zC!{1KFeKUSuU`K6XYM|u0^|J*T?R2$HFc~;ysR=jJ_=k6ffKp*GcZ8a{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awkrI&pv2VFQ6<_SW?ayiZ)wY@X)CRl324
z!*iR!iGWF_y9;`BC*3&q>CwUq>{@q!bJWh`i~a2yc3do~qjksplJ9fh`!TRpw6=fS
zefZkOcELEA-#d;jf6Vvn-sZ=8Zui<B%eB=#+0j4o{}GS%)rT)H4voEWSY~RnJby*`
z9mkivGUYS>pDpA+ByaUD`Q7@BZjC~`n=_W22yjm5k@&tv(8=SBhqXqP(JftnZHaYF
zfw%5u7xAs>-n6o0*3~4L``sE-j5h|CZoAzz)zEcK=vwYJNiC7B?kpAo*+2dZ=S#iL
zJi)0QxX1SL?Br8gdfdyxJ{nqN1kVdee)Vk1l2taZYoyw~{L{SZ?`gJe@~vB4myY-)
zI&~hH^n3r-?OC0yrIUVFZ@M8;xmR<QiQq*Q5$})D{UxVe6`D_{tT#Jmn<^T-C0}ce
z%Pr0=wF&b>+TLp%3h>i&ja@xSzm{wM3kFEsBqXFYBuKKku?cKo{3LO(MvRSB;Ve^&
z!oEZQ{|kKj@4Ui`&CQHWLReiv#r_H-Lu4Y`Ww5?9hUWigdE6YjCNlCeFldA_?gX2c
zl8~5~kdiV*SI^Vu+%?~mI(jEO*XZc2Jmcx>uXEz;8Dm2OgM@^Xq=b|U{|=u2;5_SN
zJI`hQ$Y=6GI<_`FcO4FB9WH2yZ)<dPXnVK;m<-x^4reQ}rA=TnWm9jnc>bs9)S@$A
zdW2YeQ+!#KnVpSU?;n}O6EDwt{L!O7PtF{fbmosu&9uj*5&{NB28Kpjn&t2R#_gV7
z`2ODf=x;{vMSrV){QJ<JbKBni*SqW1|K0htT-RFu{(7m2c^C8K^cd_L{j=fWlETpB
zU6#M&;FpJ=wG&Jj{AO?{GQiXPgQSWBD~<~sNH|>8-e^+5<drS7d`>;1U{_B=h18U`
zjfo7gcQ~xmfC>CZwdjd$i4PJ4m@jEL%o5CDQkGcsDWqXWyJLrAlOx~q)e2g!4O$@w
ZbnG_l6bO{)WnuK~<<VeZ21XDF0|4v8v^D?$
new file mode 100644
index 0000000000000000000000000000000000000000..173907405bdd3af04d7f563d4903ab3d995f899c
GIT binary patch
literal 1136
zc$_QucXRU(3GruOU@2hWW&i^ABMgiTSb?*fn*sv^vj&j46Nu06KgiGO<`&`$6ng^X
zO9HV>`LXU8U&jy+pm`uI=?n~v5ln2_>-~fEjX-Rmm>m!&@N)@#NX|_xU|?Xn0pyzl
z<(wFJnU7_pCZ+({96&KGAU0!J`oJ^;C<qiY0rI7RScS=-^?pWfNd?f33?Lt*mV^BQ
z14C|N1<*Xk03e?YjF|!$@)C1Xfnqa&7-%ok6u!jtR|WaSB|tX|0M&>AvBLj02IGo3
z$q6Y52@H?=uCBWF@sC-&<c^u~46YJ-vUdBKX0|c$v8f5LGT7hZ0{RxJ_Rpaot%_Yg
zS(#Z3j21AdF+|xggPaJoo(ZBRvF@NnW8>7u#+BWf{LIYG{4HrU2NF)4U=c}8c$(6{
z=*CuZ;lKhQ{L%W6+gYSdY5@<kGJ~cZrw~a0+zETL4jYKH_J0b_XrHu@f5FlT4zf#l
z6i<pVNqX&RTX%>hx+UV^wMlFFFRE*lyuGxs`u>9Vmy;t~gSc8fI)1-=b;q7@$1!b|
zTczE;DUFQtl*$WQzn_S`!zsD5y|G~8?<dbsh(Gbya(zE(`)`4(8yZ|hcQ#A!ae4QM
zQGR0e1f||P>h^B?6!s^wwk!LcNt(F0A^n)5QBQ_?z=au;f+Y5dY>~b<ZN*O6P_rMp
zJ6XcsRjkU1tFS!dR<!6#$g*SNFWvNB-%7n!@mgcGWTM1w_d{K6SNYEbBpu5->-N@n
zn}1QU&5fdEBDVFX>bR~gWR}ly{i#=Czsx7;;OF?WVM`64?|$}ko=kT5udS;;&7IRT
zHRi<qy%(20@AR9Md0r~_beuNZwt9_bpZ0UN_ieZvYWm+-Yw2;{gT<?A7^lt1$%KS|
zLPA<Yf+U+8o4^LfPZ9@f#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~L?*Ib
z2J1^>X#Rhe$IYQ@A|o#YgGMOhPOy0?35kgbDJdd4dY(S#uKD`woH%=Cvw?v@LPAPX
zLdu1I2hV?Sp7pVv=Q4lfGkGB$TbrJ{4hOUj8??u_H99)9CGG=8Z#&Q7LPfT;32dfp
z#%$)#|1_OibmmKs5NmIWFRL=MvoY)aBa?XI<yntEdi3YXnIn_V{IMzFHk4st>EZF|
z>G3*sw|f8ecd2)Fe}6u0@0seOb)Gf#Km40=-@pI5=zey6-p|<6=Z=40?Y1O(%j|Y;
zhUIhY(->f3k;2gAU6#M&;FpJ=wG&Jj{AO?{GQiXFgQSWBD~<~sNH|>8-e^+5<drQH
zruE)o$xJr~8(y!&Hw+r|@>*n#yNnF}?Bz<iZBSs)!I&wkaF)ZYQIIFFQp@4YVTB_K
l2Nl{chYCzpauC%@5R<<V)1lGh*2KW($$f)?`2_<Q0|5V|rh@<g
new file mode 100644
index 0000000000000000000000000000000000000000..c8db7da1f71085fa192009f054c896c03f68593a
GIT binary patch
literal 1188
zc$_QucXRU(3GruOU|GVz%>V=(PZ$^(umWc{Hw6X;W)C3qBoP0vD>QL+a|`hW>J<U<
zC4pF`{8)F4uVV;M4T}MgpUJ?$7{SCQq3$27Zv<ik#q5AMfuBp@Lvn6n0Rscm4<O$h
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ6G36L)h#41ewtoJi=ODceN%mFzVgxN1J
zFytmy0L^0z0P@+um??lEFEKY2C^iF#6M$}SNn+gERghm?0%U{SCkDg{|JxXhE9N98
zq$DITJQ7(~@w%dJ_IjQ>XY?DCc&78sk8kvGW@Kj;?qFh2=jA-azyMYI=g^N<#jc;M
z%q#{*3z*axqHLH!P6S%d1W}V%chI7-acX1Z%I-{lW@cypmb97!2`5glh@>VwO=)0s
zV=K9EU;z;RX#L3TEYc>mfQMO`K~s)X2&8}Sr1M^f9R!Z;UsCVD^6|~ly&7*7Rd0JO
zJnFpULi3jE_mXFsd3&3uY)`m%O#Q=>{zn(fKYDM96BU`2I`jU!Gv+gAvK(NId$_PN
zZ$rTw#|LLO_G`btQ^@`OUiV|Omio6ln!W#@ES&!Reb1L$={C04R(09BXvqBDak%}l
z*u1^b$8%loEq~1S*`;rHM@_S&Nzs%ui#E8joz|-MxUgJSspZIwEiOL(Zo416WNh;`
z_PYALtA-)XN}qe{*)=azYYlmxM(8BZ))jwrP-5<i8(L=#oLzn1@-eQg`aMgM=k2`j
zsc-d9YHBV{Km2*(J;Bv0#d>-t$0n5S3sbv)eCfugcBV6L-AK0Ma(4Tp`PQHFd1?E$
zZLH6yT)Qk8pl0y&U%tPuSSRP#Nx!Q<&3N5B_jS_ju7?UD!5^dF^GuxVz;^YEztJnv
zqpVNQ+ACzuT%&&ZOKWJ9L|wUHTTy^$Mc1l*3@c`Ak^sd|LPA<Yf+U+8o4^LfPZ9@f
z#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~L?*Ib2FGg}L-YT$JZ=tM6B&6K
z7&Jl|cY@<PB_S~}Atfb5N6*ve+%?^k`W`2B^iFuL(a~FZ##8s)2_J1uT>~=%gM@^X
zq=b|U{|=u2;5_SNJI`hQ$Y=6GI<_`FcO4FB9WH2#Z)<dPXnVK^m>k-94woykrA=Tn
zWixLxdH$#A)S@$AdW2YeQ+!#KnVpSU?;n}O6EDwt{L!O7PtF{fbmotZ6Q9Bh2L)gP
zO-cy~dUx;l-ZK5i_x9Rvf0KGo`<wTp--qjka;odsi~o)LU3~g|RGHnrcvGdji|*RQ
zFkF5b3d(elut{NP@-EBYaq!E-&)Nwl41O~>6dB-Y|3Om4ffdIE4kR3|YHu_tVDidN
zn*Qvb<DyE11ePP07nt<$GQW7_cEfP72J_+js~xuSGP5^JObBDV)RQ1AaZq6Da;76T
pJWf19JVh3<tbv+LAq$z(J~p0h?KqU7!oiUJmd6HY7xN1U1^~6(!`J`-
new file mode 100644
index 0000000000000000000000000000000000000000..85879065951ac466e6b018339d8ede5cc7ca2793
GIT binary patch
literal 1156
zc$_QucXRU(3GruOU}<6CW&i>X9tK7RtiajLO@V=d*#yYk3&giWjAN?Y+(LYTVqbuK
zNg$RfKh_=N>loqzG*1A?&tza=j9_BZpY9*5Zv<ik#q5AMfuBp@Lvn6n0Rscm6CmFl
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ4w$X;n6R$=mIy`Pa=QUSE12IOE6X1~C|
zkegTmG><U=$Y%p%rT~V##N1S%*bE@{1lo1MjkCnRAiuZ-=w=z98ZjVN_}|80Trnp(
zAtfP!;Sqo8%l^kdbN3k)81HB3GKjINsbe+bWtHLaQQ%@IILV#GzyMYI=g^N<#jc;M
z%q#{*3z*axqHMrH1GJtAq9(EKphaWj)W*h@-I@H%%+CBRX*CBDPMly7Nlkc~(!l7(
zR&wFM0wDa+`jOjNq)lo853@3ZrW~gbNdM9ad$SH3h_v>9Vwa7wN_n$j=>#6Wpf(31
zQx>BsDQ9DnCq=L*T@Pxr@&3)Tw04q{`~vUzCC4|N2=LN8a%kWEy>{W>*$;@uDFoPW
zk8lxt@yN2MdDiz6z8Zz%J6qRPblo|&^+(4Y$L)olOEY3G?B<UY<^B@jmiK@yf8y+l
z1L;r1sva2@NeAt5duQ;kRZ@pbrn38>n5B)%`!g4o%Q`tGyF@8Hoj<3{F`w&r@=7Dc
zyc5+4Y2C@0+jdUhyGFm=tN)47*Plg-%g^0Bv*cdHDH-FSXYVFVSoNyUTsr54?2F#W
ztyTqgXUjUbx1K(GOulPlP|v@apMq`p=l)FaKDV7Om{()!j1PbF^|wY{_W$AQH~Zk7
zcYp6pyS-J0Z_1n#_p8z(`i19)NLyC_5lz>a8T3AeZ6%-Z)!L-3da~bjre11Z^<$^#
ze#Yet^=lX)agmUa){r2{=Ef$lf$@{X!5T3(R)w=nEeiV%{r@lU>A&*|FE%$bHVI*M
z1r_@%j0};9Y?s0M(iocmpXG6L=$gpL%fO%!%D5A3UP?k@VnRxah_0Te&$(;5C-pVX
zo$%4t)ZJiWV33fIl9Z5g;orgYADm}>Z0EVmANfpPNXOQu=dQy6t-~9d;@cV>9oiDN
z0;9W~=WwbbTiOIRQ#Nfj`{#d}PAxj~rALUhH^rA#nc3Nx_5P7bJn{0Z#~(fV^W@Bt
zNoW4p<V;H{<q$A1GB7mK(ky@fH*WXz!uR*)M}ISVFZx^c<KKt&oZI&9zusN9{_oDG
z<+|4L_t#5J%)6K;r^isL?y3zBj}(R`@3Q<I2fsZ0tes%O;5UOqkpZ5zA0$;ASaDq7
zK*Hgw_C}KeCa>%yFU{-;8DcyVj2}%jc#{t^?Ajq%c1GjWfe+ucCgdJIa8QA5Noa$K
z@(mU@1DB^O84UTG1)5o!W&FY&L%JA3S2RS{Bor$yGLYhAV)&88eFSJ1^9u+D0Bt*@
A#Q*>R
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/mochitest.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+
+[test_fraction-parameters.html]
+support-files =
+  fonts/fraction-axisheight7000-rulethickness1000.woff
+  fonts/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff
+  fonts/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff
+  fonts/fraction-denominatorgapmin4000-rulethickness1000.woff
+  fonts/fraction-denominatorshiftdown3000-rulethickness1000.woff
+  fonts/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff
+  fonts/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff
+  fonts/fraction-numeratorgapmin9000-rulethickness1000.woff
+  fonts/fraction-numeratorshiftup11000-rulethickness1000.woff
+  fonts/fraction-rulethickness10000.woff
+[test_stack-parameters.html]
+support-files =
+  fonts/stack-axisheight7000.woff
+  fonts/stack-bottomdisplaystyleshiftdown5000.woff
+  fonts/stack-bottomshiftdown6000.woff
+  fonts/stack-displaystylegapmin4000.woff
+  fonts/stack-gapmin8000.woff
+  fonts/stack-topdisplaystyleshiftup3000.woff
+  fonts/stack-topshiftup9000.woff
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/test_fraction-parameters.html
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Fraction parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the fraction parameters from the MATH table.">
+<!-- Copyright 2016 MathML Association
+     Licensed under 3-Clause BSD-License -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight7000-rulethickness1000;
+    src: url("fonts/fraction-axisheight7000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatordisplaystylegapmin5000-rulethickness1000;
+    src: url("fonts/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;
+    src: url("fonts/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatorgapmin4000-rulethickness1000;
+    src: url("fonts/fraction-denominatorgapmin4000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatorshiftdown3000-rulethickness1000;
+    src: url("fonts/fraction-denominatorshiftdown3000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratordisplaystylegapmin8000-rulethickness1000;
+    src: url("fonts/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratordisplaystyleshiftup2000-rulethickness1000;
+    src: url("fonts/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratorgapmin9000-rulethickness1000;
+    src: url("fonts/fraction-numeratorgapmin9000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratorshiftup11000-rulethickness1000;
+    src: url("fonts/fraction-numeratorshiftup11000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: rulethickness10000;
+    src: url("fonts/fraction-rulethickness10000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  var test_loaded = async_test("Page Loaded");
+  window.addEventListener("load", function() {
+    // FIXME: Use an API to wait for the Web fonts to arrive.
+    window.setTimeout(runTests, 250);
+  });
+
+  function runTests() {
+    test(function() {
+      var v1 = 7000 * emToPx;
+      var v2 = 1000 * emToPx;
+      assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+                           v1 + v2 / 2, epsilon, "mfrac: axis height");
+    }, "AxisHeight");
+
+    test(function() {
+      var v1 = 5000 * emToPx;
+      assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+                           v1, epsilon, "mfrac: denominator gap");
+    }, "DenominatorDisplayStyleGapMin");
+
+    test(function() {
+      var v1 = 6000 * emToPx;
+      assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+                           v1, epsilon, "mfrac: denominator shift");
+    }, "DenominatorDisplayStyleShiftDown");
+
+    test(function() {
+      var v1 = 4000 * emToPx;
+      assert_approx_equals(getBox("den0004").top - getBox("ref0004").bottom,
+                           v1, epsilon, "mfrac: denominator gap");
+    }, "DenominatorGapMin");
+
+    test(function() {
+      var v1 = 3000 * emToPx;
+      assert_approx_equals(getBox("den0005").top - getBox("ref0005").bottom,
+                           v1, epsilon, "mfrac: denominator shift");
+    }, "DenominatorShiftDown");
+
+    test(function() {
+      var v1 = 8000 * emToPx;
+      assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+                           v1, epsilon, "mfrac: numerator gap");
+    }, "NumeratorDisplayStyleGapMin");
+
+    test(function() {
+      var v1 = 2000 * emToPx;
+      assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+                           v1, epsilon, "mfrac: numerator shift");
+    }, "NumeratorDisplayStyleShiftDown");
+
+    test(function() {
+      var v1 = 9000 * emToPx;
+      assert_approx_equals(getBox("ref0008").top - getBox("num0008").bottom,
+                           v1, epsilon, "mfrac: numerator gap");
+    }, "NumeratorGapMin");
+
+    test(function() {
+      var v1 = 11000 * emToPx;
+      assert_approx_equals(getBox("ref0009").top - getBox("num0009").bottom,
+                           v1, epsilon, "mfrac: numerator shift");
+    }, "NumeratorShiftDown");
+
+    test(function() {
+      var v1 = 10000 * emToPx;
+      assert_approx_equals(getBox("den0010").top - getBox("num0010").bottom,
+                           v1, epsilon, "mfrac: rule thickness");
+    }, "FractionRuleThickness");
+
+    test_loaded.done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight7000-rulethickness1000;">
+      <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: denominatordisplaystylegapmin5000-rulethickness1000;">
+      <mspace id="ref0002" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" height="1em" id="den0002" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;">
+      <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: denominatorgapmin4000-rulethickness1000;">
+      <mspace id="ref0004" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" height="1em" id="den0004" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: denominatorshiftdown3000-rulethickness1000;">
+      <mspace id="ref0005" width="3em" height="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0005" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: numeratordisplaystylegapmin8000-rulethickness1000;">
+      <mspace id="ref0006" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" depth="1em" id="num0006" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: numeratordisplaystyleshiftup2000-rulethickness1000;">
+      <mspace id="ref0007" width="3em"
+              depth="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: numeratorgapmin9000-rulethickness1000;">
+      <mspace id="ref0008" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" depth="1em" id="num0008" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: numeratorshiftup11000-rulethickness1000;">
+      <mspace id="ref0009" width="3em"
+              depth="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0009" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: rulethickness10000">
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0010" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0010" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/test_stack-parameters.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Stack parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
+<!-- Copyright 2016 MathML Association
+     Licensed under 3-Clause BSD-License -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight7000;
+    src: url("fonts/stack-axisheight7000.woff");
+  }
+  @font-face {
+    font-family: bottomdisplaystyleshiftdown5000;
+    src: url("fonts/stack-bottomdisplaystyleshiftdown5000.woff");
+  }
+  @font-face {
+    font-family: bottomshiftdown6000;
+    src: url("fonts/stack-bottomshiftdown6000.woff");
+  }
+  @font-face {
+    font-family: displaystylegapmin4000;
+    src: url("fonts/stack-displaystylegapmin4000.woff");
+  }
+  @font-face {
+    font-family: gapmin8000;
+    src: url("fonts/stack-gapmin8000.woff");
+  }
+  @font-face {
+    font-family: topdisplaystyleshiftup3000;
+    src: url("fonts/stack-topdisplaystyleshiftup3000.woff");
+  }
+  @font-face {
+    font-family: topshiftup9000;
+    src: url("fonts/stack-topshiftup9000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  var test_loaded = async_test("Page Loaded");
+  window.addEventListener("load", function() {
+    // FIXME: Use an API to wait for the Web fonts to arrive.
+    window.setTimeout(runTests, 250);
+  });
+
+  function runTests() {
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+                           v, epsilon, "mfrac: axis height");
+    }, "AxisHeight");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+                           v, epsilon, "mfrac: denominator shift");
+    }, "BottomDisplayStyleShiftDown");
+
+    test(function() {
+      var v = 6000 * emToPx;
+      assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+                           v, epsilon, "mfrac: denominator shift");
+    }, "BottomShiftDown");
+
+    test(function() {
+      var v = 4000 * emToPx;
+      assert_approx_equals(getBox("den0004").top - getBox("num0004").bottom,
+                           v, epsilon, "mfrac: gap");
+    }, "DisplayStyleGapMin");
+
+    test(function() {
+      var v = 8000 * emToPx;
+      assert_approx_equals(getBox("den0005").top - getBox("num0005").bottom,
+                           v, epsilon, "mfrac: gap");
+    }, "GapMin");
+
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+                           v, epsilon, "mfrac: numerator shift");
+    }, "TopDisplayStyleShiftUp");
+
+    test(function() {
+      var v = 9000 * emToPx;
+      assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+                           v, epsilon, "mfrac: numerator shift");
+    }, "ToShiftUp");
+
+    test_loaded.done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight7000;">
+      <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: bottomdisplaystyleshiftdown5000;">
+      <mspace id="ref0002" width="3em" height="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0002" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: bottomshiftdown6000;">
+      <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: displaystylegapmin4000;">
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0004" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0004" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: gapmin8000;">
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0005" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0005" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: topdisplaystyleshiftup3000;">
+      <mspace id="ref0006" width="3em" depth="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0006" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: topshiftup9000;">
+      <mspace id="ref0007" width="3em" depth="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
--- a/layout/mathml/moz.build
+++ b/layout/mathml/moz.build
@@ -3,17 +3,20 @@
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'MathML')
 
 if CONFIG['ENABLE_TESTS']:
-    MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+    MOCHITEST_MANIFESTS += [
+        'imptests/mochitest.ini',
+        'tests/mochitest.ini',
+]
 
 UNIFIED_SOURCES += [
     'nsMathMLChar.cpp',
     'nsMathMLContainerFrame.cpp',
     'nsMathMLFrame.cpp',
     'nsMathMLmactionFrame.cpp',
     'nsMathMLmencloseFrame.cpp',
     'nsMathMLmfencedFrame.cpp',
--- a/layout/mathml/nsMathMLmfracFrame.cpp
+++ b/layout/mathml/nsMathMLmfracFrame.cpp
@@ -328,16 +328,21 @@ nsMathMLmfracFrame::PlaceInternal(DrawTa
         7 * defaultRuleThickness : 3 * defaultRuleThickness;
       if (mathFont) {
         minClearance =
           mathFont->GetMathConstant(displayStyle ?
                                     gfxFontEntry::StackDisplayStyleGapMin :
                                     gfxFontEntry::StackGapMin,
                                     oneDevPixel);
       }
+      // Factor in axis height
+      // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2
+      numShift += axisHeight;
+      denShift += axisHeight;
+
       nscoord actualClearance =
         (numShift - bmNum.descent) - (bmDen.ascent - denShift);
       // actualClearance should be >= minClearance
       if (actualClearance < minClearance) {
         nscoord halfGap = (minClearance - actualClearance)/2;
         numShift += halfGap;
         denShift += halfGap;
       }
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -164,16 +164,17 @@ CSS_KEY(bold-script, bold_script)
 CSS_KEY(bolder, bolder)
 CSS_KEY(border-box, border_box)
 CSS_KEY(both, both)
 CSS_KEY(bottom, bottom)
 CSS_KEY(bottom-outside, bottom_outside)
 CSS_KEY(break-all, break_all)
 CSS_KEY(break-word, break_word)
 CSS_KEY(brightness, brightness)
+CSS_KEY(browser, browser)
 CSS_KEY(bullets, bullets)
 CSS_KEY(button, button)
 CSS_KEY(buttonface, buttonface)
 CSS_KEY(buttonhighlight, buttonhighlight)
 CSS_KEY(buttonshadow, buttonshadow)
 CSS_KEY(buttontext, buttontext)
 CSS_KEY(capitalize, capitalize)
 CSS_KEY(caption, caption)
@@ -275,16 +276,17 @@ CSS_KEY(flat, flat)
 CSS_KEY(flex, flex)
 CSS_KEY(flex-end, flex_end)
 CSS_KEY(flex-start, flex_start)
 CSS_KEY(flip, flip)
 CSS_KEY(forwards, forwards)
 CSS_KEY(fraktur, fraktur)
 CSS_KEY(from-image, from_image)
 CSS_KEY(full-width, full_width)
+CSS_KEY(fullscreen, fullscreen)
 CSS_KEY(grab, grab)
 CSS_KEY(grabbing, grabbing)
 CSS_KEY(grad, grad)
 CSS_KEY(grayscale, grayscale)
 CSS_KEY(graytext, graytext)
 CSS_KEY(grid, grid)
 CSS_KEY(groove, groove)
 CSS_KEY(hard-light, hard_light)
@@ -365,16 +367,17 @@ CSS_KEY(ltr, ltr)
 CSS_KEY(luminance, luminance)
 CSS_KEY(luminosity, luminosity)
 CSS_KEY(mandatory, mandatory)
 CSS_KEY(manipulation, manipulation)
 CSS_KEY(manual, manual)
 CSS_KEY(margin-box, margin_box)
 CSS_KEY(markers, markers)
 CSS_KEY(match-parent, match_parent)
+CSS_KEY(match-source, match_source)
 CSS_KEY(matrix, matrix)
 CSS_KEY(matrix3d, matrix3d)
 CSS_KEY(max-content, max_content)
 CSS_KEY(medium, medium)
 CSS_KEY(menu, menu)
 CSS_KEY(menutext, menutext)
 CSS_KEY(message-box, message_box)
 CSS_KEY(middle, middle)
@@ -530,16 +533,17 @@ CSS_KEY(space-around, space_around)
 CSS_KEY(space-between, space_between)
 CSS_KEY(space-evenly, space_evenly)
 CSS_KEY(span, span)
 CSS_KEY(spell-out, spell_out)
 CSS_KEY(square, square)
 CSS_KEY(stacked-fractions, stacked_fractions)
 CSS_KEY(start, start)
 CSS_KEY(static, static)
+CSS_KEY(standalone, standalone)
 CSS_KEY(status-bar, status_bar)
 CSS_KEY(step-end, step_end)
 CSS_KEY(step-start, step_start)
 CSS_KEY(sticky, sticky)
 CSS_KEY(stretch, stretch)
 CSS_KEY(stretch-to-fit, stretch_to_fit)
 CSS_KEY(stretched, stretched)
 CSS_KEY(strict, strict)
@@ -701,16 +705,17 @@ CSS_KEY(menuarrow, menuarrow)
 CSS_KEY(menuimage, menuimage)
 CSS_KEY(menuitemtext, menuitemtext)
 CSS_KEY(menulist, menulist)
 CSS_KEY(menulist-button, menulist_button)
 CSS_KEY(menulist-text, menulist_text)
 CSS_KEY(menulist-textfield, menulist_textfield)
 CSS_KEY(meterbar, meterbar)
 CSS_KEY(meterchunk, meterchunk)
+CSS_KEY(minimal-ui, minimal_ui)
 CSS_KEY(range, range)
 CSS_KEY(range-thumb, range_thumb)
 CSS_KEY(sans-serif, sans_serif)
 CSS_KEY(sans-serif-bold-italic, sans_serif_bold_italic)
 CSS_KEY(sans-serif-italic, sans_serif_italic)
 CSS_KEY(scale-horizontal, scale_horizontal)
 CSS_KEY(scale-vertical, scale_vertical)
 CSS_KEY(scalethumb-horizontal, scalethumb_horizontal)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -11856,17 +11856,17 @@ CSSParserImpl::ParseImageLayersItem(
   RefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
   aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
   positionArr->Item(1).SetPercentValue(0.0f);
   positionArr->Item(3).SetPercentValue(0.0f);
   aState.mSize->mXValue.SetAutoValue();
   aState.mSize->mYValue.SetAutoValue();
   aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
                                         eCSSUnit_Enumerated);
-  aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_AUTO,
+  aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
                                    eCSSUnit_Enumerated);
   bool haveColor = false,
        haveImage = false,
        haveRepeat = false,
        haveAttach = false,
        havePositionAndSize = false,
        haveOrigin = false,
        haveComposite = false,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -928,20 +928,17 @@ const KTableEntry nsCSSProps::kImageLaye
   { eCSSKeyword_contain, NS_STYLE_IMAGELAYER_SIZE_CONTAIN },
   { eCSSKeyword_cover,   NS_STYLE_IMAGELAYER_SIZE_COVER },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kImageLayerModeKTable[] = {
   { eCSSKeyword_alpha, NS_STYLE_MASK_MODE_ALPHA },
   { eCSSKeyword_luminance, NS_STYLE_MASK_MODE_LUMINANCE },
-  // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1224424
-  // It's ambigious at mask shorthand parsing while we have both mask-mode:auto
-  // and mask-size:auto.
-  { eCSSKeyword_auto, NS_STYLE_MASK_MODE_AUTO },
+  { eCSSKeyword_match_source, NS_STYLE_MASK_MODE_MATCH_SOURCE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kImageLayerCompositeKTable[] = {
   { eCSSKeyword_add, NS_STYLE_MASK_COMPOSITE_ADD },
   { eCSSKeyword_substract, NS_STYLE_MASK_COMPOSITE_SUBSTRACT },
   { eCSSKeyword_intersect, NS_STYLE_MASK_COMPOSITE_INTERSECT },
   { eCSSKeyword_exclude, NS_STYLE_MASK_COMPOSITE_EXCLUDE },
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -6013,17 +6013,17 @@ nsComputedDOMStyle::DoGetMask()
 
   // Mask is now a shorthand, but it used to be a longhand, so that we
   // need to support computed style for the cases where it used  to be
   // a longhand.
   if (svg->mMask.mImageCount > 1 ||
       firstLayer.mClip != NS_STYLE_IMAGELAYER_CLIP_BORDER ||
       firstLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING ||
       firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD ||
-      firstLayer.mMaskMode != NS_STYLE_MASK_MODE_AUTO ||
+      firstLayer.mMaskMode != NS_STYLE_MASK_MODE_MATCH_SOURCE ||
       !firstLayer.mPosition.IsInitialValue() ||
       !firstLayer.mRepeat.IsInitialValue() ||
       !firstLayer.mSize.IsInitialValue() ||
       !(firstLayer.mImage.GetType() == eStyleImageType_Null ||
           firstLayer.mImage.GetType() == eStyleImageType_Image)){
     return nullptr;
   }
 
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -12,16 +12,17 @@
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
 #include "nsCSSValue.h"
 #ifdef XP_WIN
 #include "mozilla/LookAndFeel.h"
 #endif
 #include "nsCSSRuleProcessor.h"
 #include "nsDeviceContext.h"
+#include "nsIBaseWindow.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "mozilla/StyleSheetHandle.h"
 #include "mozilla/StyleSheetHandleInlines.h"
 
 using namespace mozilla;
 
 static const nsCSSProps::KTableEntry kOrientationKeywords[] = {
@@ -31,16 +32,24 @@ static const nsCSSProps::KTableEntry kOr
 };
 
 static const nsCSSProps::KTableEntry kScanKeywords[] = {
   { eCSSKeyword_progressive,              NS_STYLE_SCAN_PROGRESSIVE },
   { eCSSKeyword_interlace,                NS_STYLE_SCAN_INTERLACE },
   { eCSSKeyword_UNKNOWN,                  -1 }
 };
 
+static const nsCSSProps::KTableEntry kDisplayModeKeywords[] = {
+  { eCSSKeyword_browser,                 NS_STYLE_DISPLAY_MODE_BROWSER },
+  { eCSSKeyword_minimal_ui,              NS_STYLE_DISPLAY_MODE_MINIMAL_UI },
+  { eCSSKeyword_standalone,              NS_STYLE_DISPLAY_MODE_STANDALONE },
+  { eCSSKeyword_fullscreen,              NS_STYLE_DISPLAY_MODE_FULLSCREEN },
+  { eCSSKeyword_UNKNOWN,                 -1 }
+};
+
 #ifdef XP_WIN
 struct WindowsThemeName {
   LookAndFeel::WindowsTheme id;
   const wchar_t* name;
 };
 
 // Windows theme identities used in the -moz-windows-theme media query.
 const WindowsThemeName themeStrings[] = {
@@ -300,16 +309,44 @@ GetScan(nsPresContext* aPresContext, con
 {
   // Since Gecko doesn't support the 'tv' media type, the 'scan'
   // feature is never present.
   aResult.Reset();
   return NS_OK;
 }
 
 static nsresult
+GetDisplayMode(nsPresContext* aPresContext, const nsMediaFeature*,
+               nsCSSValue& aResult)
+{
+  nsCOMPtr<nsISupports> container = aPresContext->GetRootPresContext()->
+    Document()->GetContainer();
+  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+  if (!baseWindow) {
+    aResult.SetIntValue(NS_STYLE_DISPLAY_MODE_BROWSER, eCSSUnit_Enumerated);
+    return NS_OK;
+  }
+  nsCOMPtr<nsIWidget> mainWidget;
+  baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+  int32_t displayMode;
+  nsSizeMode mode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
+  switch (mode) {
+    case nsSizeMode_Fullscreen:
+      displayMode = NS_STYLE_DISPLAY_MODE_FULLSCREEN;
+      break;
+    default:
+      displayMode = NS_STYLE_DISPLAY_MODE_BROWSER;
+      break;
+  }
+
+  aResult.SetIntValue(displayMode, eCSSUnit_Enumerated);
+  return NS_OK;
+}
+
+static nsresult
 GetGrid(nsPresContext* aPresContext, const nsMediaFeature*,
         nsCSSValue& aResult)
 {
   // Gecko doesn't support grid devices (e.g., ttys), so the 'grid'
   // feature is always 0.
   aResult.SetIntValue(0, eCSSUnit_Integer);
   return NS_OK;
 }
@@ -528,16 +565,24 @@ nsMediaFeatures::features[] = {
   {
     &nsGkAtoms::grid,
     nsMediaFeature::eMinMaxNotAllowed,
     nsMediaFeature::eBoolInteger,
     nsMediaFeature::eNoRequirements,
     { nullptr },
     GetGrid
   },
+  {
+    &nsGkAtoms::displayMode,
+    nsMediaFeature::eMinMaxNotAllowed,
+    nsMediaFeature::eEnumerated,
+    nsMediaFeature::eNoRequirements,
+    { kDisplayModeKeywords },
+    GetDisplayMode
+  },
 
   // Webkit extensions that we support for de-facto web compatibility
   // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):
   {
     &nsGkAtoms::devicePixelRatio,
     nsMediaFeature::eMinMaxAllowed,
     nsMediaFeature::eFloat,
     nsMediaFeature::eHasWebkitPrefix |
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -9832,17 +9832,17 @@ nsRuleNode::ComputeSVGResetData(void* aS
                         svgReset->mMask.mSizeCount, maxItemCount, rebuild,
                         conditions);
 
   // mask-mode: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForMaskMode(),
                     svgReset->mMask.mLayers,
                     parentSVGReset->mMask.mLayers,
                     &nsStyleImageLayers::Layer::mMaskMode,
-                    uint8_t(NS_STYLE_MASK_MODE_AUTO),
+                    uint8_t(NS_STYLE_MASK_MODE_MATCH_SOURCE),
                     parentSVGReset->mMask.mMaskModeCount,
                     svgReset->mMask.mMaskModeCount, maxItemCount, rebuild, conditions);
 
   // mask-composite: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForMaskComposite(),
                     svgReset->mMask.mLayers,
                     parentSVGReset->mMask.mLayers,
                     &nsStyleImageLayers::Layer::mComposite,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -301,17 +301,17 @@ enum class FillMode : uint32_t;
 
 // See nsStyleImageLayers
 #define NS_STYLE_IMAGELAYER_SIZE_CONTAIN             0
 #define NS_STYLE_IMAGELAYER_SIZE_COVER               1
 
 // Mask mode
 #define NS_STYLE_MASK_MODE_ALPHA                0
 #define NS_STYLE_MASK_MODE_LUMINANCE            1
-#define NS_STYLE_MASK_MODE_AUTO                 2
+#define NS_STYLE_MASK_MODE_MATCH_SOURCE         2
 
 // See nsStyleBackground
 #define NS_STYLE_BG_INLINE_POLICY_EACH_BOX      0
 #define NS_STYLE_BG_INLINE_POLICY_CONTINUOUS    1
 #define NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX  2
 
 // See nsStyleTable
 #define NS_STYLE_BORDER_COLLAPSE                0
@@ -1200,11 +1200,17 @@ enum class FillMode : uint32_t;
 // orientation
 #define NS_STYLE_ORIENTATION_PORTRAIT           0
 #define NS_STYLE_ORIENTATION_LANDSCAPE          1
 
 // scan
 #define NS_STYLE_SCAN_PROGRESSIVE               0
 #define NS_STYLE_SCAN_INTERLACE                 1
 
+// display-mode
+#define NS_STYLE_DISPLAY_MODE_BROWSER           0
+#define NS_STYLE_DISPLAY_MODE_MINIMAL_UI        1
+#define NS_STYLE_DISPLAY_MODE_STANDALONE        2
+#define NS_STYLE_DISPLAY_MODE_FULLSCREEN        3
+
 } // namespace mozilla
 
 #endif /* nsStyleConsts_h___ */
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2518,17 +2518,17 @@ nsStyleImageLayers::Repeat::SetInitialVa
 }
 
 nsStyleImageLayers::Layer::Layer()
 : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER),
   mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
   mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL),
   mBlendMode(NS_STYLE_BLEND_NORMAL),
   mComposite(NS_STYLE_MASK_COMPOSITE_ADD),
-  mMaskMode(NS_STYLE_MASK_MODE_AUTO)
+  mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE)
 {
   mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%"
   mImage.SetNull();
   mRepeat.SetInitialValues();
   mSize.SetInitialValues();
 }
 
 nsStyleImageLayers::Layer::~Layer()
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -577,17 +577,17 @@ struct nsStyleImageLayers {
                                   // For a background layer, it should always
                                   // be the initial value, which is
                                   // NS_STYLE_COMPOSITE_MODE_ADD.
     uint8_t       mMaskMode;      // [reset] See nsStyleConsts.h
                                   // mask-only property
                                   // This property is used for mask layer only.
                                   // For a background layer, it should always
                                   // be the initial value, which is
-                                  // NS_STYLE_MASK_MODE_AUTO.
+                                  // NS_STYLE_MASK_MODE_MATCH_SOURCE.
     Repeat        mRepeat;        // [reset] See nsStyleConsts.h
 
     // Initializes only mImage
     Layer();
     ~Layer();
 
     // Register/unregister images with the document. We do this only
     // after the dust has settled in ComputeBackgroundData.
--- a/layout/style/test/chrome/chrome.ini
+++ b/layout/style/test/chrome/chrome.ini
@@ -11,11 +11,12 @@ support-files =
 
 [test_addSheet.html]
 [test_additional_sheets.html]
 [test_author_specified_style.html]
 [test_bug418986-2.xul]
 [test_bug1157097.html]
 [test_bug1160724.xul]
 [test_bug535806.xul]
+[test_display_mode.html]
 [test_hover.html]
 skip-if = buildapp == 'mulet'
 [test_moz_document_rules.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/chrome/test_display_mode.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1104916
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Display Mode</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+/** Test for Display Mode **/
+SimpleTest.waitForExplicitFinish();
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function waitOneEvent(element, name) {
+  return new Promise(function(resolve, reject) {
+    element.addEventListener(name, function listener() {
+      element.removeEventListener(name, listener);
+      resolve();
+    });
+  });
+}
+
+add_task(function* () {
+  yield waitOneEvent(window, "load");
+
+  var iframe = document.getElementById("subdoc");
+  var subdoc = iframe.contentDocument;
+  var style = subdoc.getElementById("style");
+  var bodyComputedStyled = subdoc.defaultView.getComputedStyle(subdoc.body, "");
+  var win = Services.wm.getMostRecentWindow("navigator:browser");
+
+  function queryApplies(q) {
+    style.setAttribute("media", q);
+    return bodyComputedStyled.getPropertyValue("text-decoration") == "underline";
+  }
+
+  function shouldApply(q) {
+    ok(queryApplies(q), q + " should apply");
+  }
+
+  function shouldNotApply(q) {
+    ok(!queryApplies(q), q + " should not apply");
+  }
+
+  shouldApply("all and (display-mode: browser)");
+  shouldNotApply("all and (display-mode: fullscreen)");
+  shouldNotApply("all and (display-mode: standalone)");
+  shouldNotApply("all and (display-mode: minimal-ui)");
+
+  // Test entering the OS's fullscreen mode.
+  var fullScreenEntered = waitOneEvent(win, "sizemodechange");
+  synthesizeKey("VK_F11", {});
+  yield fullScreenEntered;
+  shouldApply("all and (display-mode: fullscreen)");
+  shouldNotApply("all and (display-mode: browser)");
+  var fullScreenExited = waitOneEvent(win, "sizemodechange");
+  synthesizeKey("VK_F11", {});
+  yield fullScreenExited;
+  shouldNotApply("all and (display-mode: fullscreen)");
+  shouldApply("all and (display-mode: browser)");
+
+  // Test entering fullscreen through document requestFullScreen.
+  fullScreenEntered = waitOneEvent(document, "mozfullscreenchange");
+  document.body.mozRequestFullScreen();
+  yield fullScreenEntered
+  ok(document.mozFullScreenElement, "window entered fullscreen");
+  shouldApply("all and (display-mode: fullscreen)");
+  shouldNotApply("all and (display-mode: browser)");
+  fullScreenExited = waitOneEvent(document, "mozfullscreenchange");
+  document.mozCancelFullScreen();
+  yield fullScreenExited;
+  ok(!document.mozFullScreenElement, "window exited fullscreen");
+  shouldNotApply("all and (display-mode: fullscreen)");
+  shouldApply("all and (display-mode: browser)");
+});
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104916">Mozilla Bug 1104916</a>
+<iframe id="subdoc" src="http://mochi.test:8888/tests/layout/style/test/media_queries_iframe.html"></iframe>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4112,19 +4112,19 @@ var gCSSProperties = {
     invalid_values: []
   },
   "mask": {
     domProp: "mask",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     /* FIXME: All mask-border-* should be added when we implement them. */
     subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite"],
-    initial_values: [ "auto", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
+    initial_values: [ "match-source", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
       "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none",
-      "auto none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add auto", "none repeat add 0% 0% / auto auto auto" ],
+      "match-source none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add match-source", "none repeat add 0% 0% / auto auto match-source" ],
     other_values: [
       "none alpha repeat add left top",
       "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)",
       "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') alpha left top add",
       "repeat-x",
       "repeat-y",
       "no-repeat",
       "none repeat-y alpha add 0% 0%",
@@ -4178,16 +4178,17 @@ var gCSSProperties = {
       "-moz-linear-gradient(10 10px -45deg, red, blue) repeat",
       "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat",
       "linear-gradient(red -99, yellow, green, blue 120%)",
       /* bug 258080: don't accept background-position separated */
       "left url(404.png) top", "top url(404.png) left",
       "alpha padding-box url(404.png) border-box",
       "alpha padding-box url(404.png) padding-box",
       "-moz-element(#a rubbish)",
+      "left top / match-source"
     ]
   },
   "mask-clip": {
     domProp: "maskClip",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "border-box" ],
     other_values: [ "content-box", "padding-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ],
@@ -4211,19 +4212,19 @@ var gCSSProperties = {
     ].concat(invalidGradientAndElementValues),
     unbalanced_values: [
     ].concat(unbalancedGradientAndElementValues)
   },
   "mask-mode": {
     domProp: "maskMode",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
-    initial_values: [ "auto" ],
-    other_values: [ "alpha", "luminance", "auto, auto", "auto, alpha", "alpha, luminance, auto"],
-    invalid_values: [ "auto auto", "alpha auto" ]
+    initial_values: [ "match-source" ],
+    other_values: [ "alpha", "luminance", "match-source, match-source", "match-source, alpha", "alpha, luminance, match-source"],
+    invalid_values: [ "match-source match-source", "alpha match-source" ]
   },
   "mask-composite": {
     domProp: "maskComposite",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "add" ],
     other_values: [ "substract", "intersect", "exclude", "add, add", "substract, intersect", "substract, substract, add"],
     invalid_values: [ "add substract", "intersect exclude" ]
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -248,16 +248,24 @@ function run() {
     expression_should_not_be_parseable("min-" + feature + ": -1px");
     expression_should_not_be_parseable("max-" + feature + ": -1px");
     expression_should_not_be_parseable(feature + ": -0.00001mm");
     expression_should_not_be_parseable(feature + ": -100000em");
     expression_should_not_be_parseable("min-" + feature);
     expression_should_not_be_parseable("max-" + feature);
   }
 
+  var mediatypes = ["browser", "minimal-ui", "standalone", "fullscreen"];
+
+  mediatypes.forEach(function(type) {
+    expression_should_be_parseable("display-mode: " + type);
+  });
+
+  expression_should_not_be_parseable("display-mode: invalid")
+
   var content_div = document.getElementById("content");
   content_div.style.font = "initial";
   var em_size =
     getComputedStyle(content_div, "").fontSize.match(/^(\d+)px$/)[1];
 
   // in this test, assume the common underlying implementation is correct
   var width_val = 117; // pick two not-too-round numbers
   var height_val = 76;
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -66,56 +66,30 @@ function doConfirm(msg)
 {
   let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
     getService(Components.interfaces.nsIPromptService);
   return prompts.confirm(window, null, msg);
 }
 
 function RefreshDeviceList()
 {
-  var modules = secmoddb.listModules();
-  var done = false;
+  let modules = secmoddb.listModules();
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(nsIPKCS11Module);
+    let slotnames = [];
+    let slots = module.listSlots();
+    while (slots.hasMoreElements()) {
+      let slot = slots.getNext().QueryInterface(nsIPKCS11Slot);
+      // Token names are preferred because NSS prefers lookup by token name.
+      slotnames.push(slot.tokenName ? slot.tokenName : slot.name);
+    }
+    AddModule(module.name, slotnames);
+  }
 
-  try {
-    modules.isDone();
-  } catch (e) { done = true; }
-  while (!done) {
-    var module = modules.currentItem().QueryInterface(nsIPKCS11Module);
-    if (module) {
-      var slotnames = [];
-      var slots = module.listSlots();
-      var slots_done = false;
-      try {
-        slots.isDone();
-      } catch (e) { slots_done = true; }
-      while (!slots_done) {
-        var slot = null;
-        try {
-          slot = slots.currentItem().QueryInterface(nsIPKCS11Slot);
-        } catch (e) { slot = null; }
-        // in the ongoing discussion of whether slot names or token names
-        // are to be shown, I've gone with token names because NSS will
-        // prefer lookup by token name.  However, the token may not be
-        // present, so maybe slot names should be listed, while token names
-        // are "remembered" for lookup?
-        if (slot != null) {
-          slotnames[slotnames.length] = slot.tokenName ? slot.tokenName
-                                                       : slot.name;
-        }
-        try {
-          slots.next();
-        } catch (e) { slots_done = true; }
-      }
-      AddModule(module.name, slotnames);
-    }
-    try {
-      modules.next();
-    } catch (e) { done = true; }
-  }
-  /* Set the text on the fips button */
+  // Set the text on the FIPS button.
   SetFIPSButton();
 }
 
 function SetFIPSButton()
 {
   var fipsButton = document.getElementById("fipsbutton");
   var label;
   if (secmoddb.isFIPSEnabled) {
--- a/security/manager/pki/resources/content/password.js
+++ b/security/manager/pki/resources/content/password.js
@@ -34,38 +34,34 @@ function onLoad()
       // as window name must be a subset of ascii, and the code was
       // previously trying to assign unicode to the window's name.
       // I checked all the places where we get a password prompt and
       // all of them pass an argument as part of this patch.
       tokenName = "";
   }
 
   if (tokenName == "") {
-     var sectokdb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
-     var tokenList = sectokdb.listTokens();
-     var enumElement;
-     let i = 0;
-     var menu = document.getElementById("tokenMenu");
-     try {
-        for (; !tokenList.isDone(); tokenList.next()) {
-           enumElement = tokenList.currentItem();
-           var token = enumElement.QueryInterface(nsIPK11Token);
-           if(token.needsLogin() || !(token.needsUserInit)) {
-              var menuItemNode = document.createElement("menuitem");
-              menuItemNode.setAttribute("value", token.tokenName);
-              menuItemNode.setAttribute("label", token.tokenName);
-              menu.firstChild.appendChild(menuItemNode);
-              if (i == 0) {
-                 menu.selectedItem = menuItemNode;
-                 tokenName = token.tokenName;
-              }
-              i++;
-           }
+    let tokenDB = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+    let tokenList = tokenDB.listTokens();
+    let i = 0;
+    let menu = document.getElementById("tokenMenu");
+    while (tokenList.hasMoreElements()) {
+      let token = tokenList.getNext().QueryInterface(nsIPK11Token);
+      if (token.needsLogin() || !(token.needsUserInit)) {
+        let menuItemNode = document.createElement("menuitem");
+        menuItemNode.setAttribute("value", token.tokenName);
+        menuItemNode.setAttribute("label", token.tokenName);
+        menu.firstChild.appendChild(menuItemNode);
+        if (i == 0) {
+          menu.selectedItem = menuItemNode;
+          tokenName = token.tokenName;
         }
-     } catch (exception) {}
+        i++;
+      }
+    }
   } else {
     var sel = document.getElementById("tokenMenu");
     sel.setAttribute("hidden", "true");
     var tag = document.getElementById("tokenName");
     tag.setAttribute("value", tokenName);
   }
 
   process();
--- a/security/manager/ssl/nsIPK11TokenDB.idl
+++ b/security/manager/ssl/nsIPK11TokenDB.idl
@@ -2,17 +2,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 "nsISupports.idl"
 
 interface nsIPK11Token;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 /**
  * The PK11 Token Database provides access to the PK11 modules
  * that are installed, and the tokens that are available.
  * Interfaces: nsIPK11TokenDB
  * Threading: ??
  */
 %{C++
@@ -33,12 +33,10 @@ interface nsIPK11TokenDB : nsISupports
   /*
    * Find a token by name
    */
   nsIPK11Token findTokenByName(in wstring tokenName);
 
   /*
    * List all tokens
    */
-  nsIEnumerator listTokens();
-
+  nsISimpleEnumerator listTokens();
 };
-
--- a/security/manager/ssl/nsIPKCS11Module.idl
+++ b/security/manager/ssl/nsIPKCS11Module.idl
@@ -2,23 +2,20 @@
  *
  * 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 "nsISupports.idl"
 
 interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 [scriptable, uuid(8a44bdf9-d1a5-4734-bd5a-34ed7fe564c2)]
 interface nsIPKCS11Module : nsISupports
 {
-
   readonly attribute wstring name;
   readonly attribute wstring libName;
 
   nsIPKCS11Slot findSlotByName(in wstring name);
 
-  nsIEnumerator listSlots();
-
+  nsISimpleEnumerator listSlots();
 };
-
--- a/security/manager/ssl/nsIPKCS11ModuleDB.idl
+++ b/security/manager/ssl/nsIPKCS11ModuleDB.idl
@@ -3,34 +3,33 @@
  * 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 "nsISupports.idl"
 
 interface nsIPKCS11Module;
 interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 %{C++
 #define NS_PKCS11MODULEDB_CONTRACTID "@mozilla.org/security/pkcs11moduledb;1"
 %}
 
 [scriptable, uuid(ff9fbcd7-9517-4334-b97a-ceed78909974)]
 interface nsIPKCS11ModuleDB : nsISupports
 {
   nsIPKCS11Module getInternal();
 
   nsIPKCS11Module getInternalFIPS();
 
   nsIPKCS11Module findModuleByName(in wstring name);
 
   nsIPKCS11Slot findSlotByName(in wstring name);
 
-  nsIEnumerator listModules();
+  nsISimpleEnumerator listModules();
 
   readonly attribute boolean canToggleFIPS;
 
   void toggleFIPSMode();
 
   readonly attribute boolean isFIPSEnabled;
 };
-
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -1,23 +1,23 @@
 /* -*- 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 "nsPK11TokenDB.h"
+
+#include "nsIMutableArray.h"
 #include "nsISupports.h"
-#include "nsISupportsArray.h"
-#include "nsIPK11TokenDB.h"
+#include "nsNSSComponent.h"
+#include "nsReadableUtils.h"
+#include "nsServiceManagerUtils.h"
 #include "prerror.h"
+#include "ScopedNSSTypes.h"
 #include "secerr.h"
-#include "nsReadableUtils.h"
-#include "nsNSSComponent.h"
-#include "nsServiceManagerUtils.h"
-
-#include "nsPK11TokenDB.h"
 
 extern PRLogModuleInfo* gPIPNSSLog;
 
 NS_IMPL_ISUPPORTS(nsPK11Token, nsIPK11Token)
 
 nsPK11Token::nsPK11Token(PK11SlotInfo *slot)
 {
   nsNSSShutDownPreventionLock locker;
@@ -442,42 +442,36 @@ FindTokenByName(const char16_t* tokenNam
   token = new nsPK11Token(slot);
   token.forget(_retval);
 
 done:
   if (slot) PK11_FreeSlot(slot);
   return rv;
 }
 
-NS_IMETHODIMP nsPK11TokenDB::ListTokens(nsIEnumerator* *_retval)
+NS_IMETHODIMP
+nsPK11TokenDB::ListTokens(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  nsCOMPtr<nsISupportsArray> array;
-  PK11SlotList *list = 0;
-  PK11SlotListElement *le;
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
 
   *_retval = nullptr;
-  nsresult rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) { goto done; }
+
+  UniquePK11SlotList list(
+    PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0));
+  if (!list) {
+    return NS_ERROR_FAILURE;
+  }
 
-  /* List all tokens, creating PK11Token objects and putting them
-   * into the array.
-   */
-  list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0);
-  if (!list) { rv = NS_ERROR_FAILURE; goto done; }
-
-  for (le = PK11_GetFirstSafe(list); le; le = PK11_GetNextSafe(list, le, false)) {
+  for (PK11SlotListElement* le = PK11_GetFirstSafe(list.get()); le;
+       le = PK11_GetNextSafe(list.get(), le, false)) {
     nsCOMPtr<nsIPK11Token> token = new nsPK11Token(le->slot);
-    rv = array->AppendElement(token);
+    nsresult rv = array->AppendElement(token, false);
     if (NS_FAILED(rv)) {
-      PK11_FreeSlotListElement(list, le);
-      rv = NS_ERROR_OUT_OF_MEMORY;
-      goto done;
+      return rv;
     }
   }
 
-  rv = array->Enumerate(_retval);
-
-done:
-  if (list) PK11_FreeSlotList(list);
-  return rv;
+  return array->Enumerate(_retval);
 }
-
--- a/security/manager/ssl/nsPKCS11Slot.cpp
+++ b/security/manager/ssl/nsPKCS11Slot.cpp
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsPKCS11Slot.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "nsCOMPtr.h"
-#include "nsISupportsArray.h"
+#include "nsIMutableArray.h"
 #include "nsPK11TokenDB.h"
 #include "secmod.h"
 
 using mozilla::LogLevel;
 
 extern PRLogModuleInfo* gPIPNSSLog;
 
 NS_IMPL_ISUPPORTS(nsPKCS11Slot, nsIPKCS11Slot)
@@ -313,44 +313,45 @@ nsPKCS11Module::FindSlotByName(const cha
   } 
   free(asciiname);
   nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
   PK11_FreeSlot(slotinfo);
   slot.forget(_retval);
   return NS_OK;
 }
 
-NS_IMETHODIMP 
-nsPKCS11Module::ListSlots(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11Module::ListSlots(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
+  if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
-  nsresult rv = NS_OK;
-  int i;
-  /* get isupports array */
-  nsCOMPtr<nsISupportsArray> array;
-  rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) return rv;
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
+
   /* applications which allow new slot creation (which Firefox now does
    * since it uses the WaitForSlotEvent call) need to hold the
    * ModuleList Read lock to prevent the slot array from changing out
    * from under it. */
-  SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
-  SECMOD_GetReadLock(lock);
-  for (i=0; i<mModule->slotCount; i++) {
+  AutoSECMODListReadLock lock;
+  for (int i = 0; i < mModule->slotCount; i++) {
     if (mModule->slots[i]) {
       nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(mModule->slots[i]);
-      array->AppendElement(slot);
+      nsresult rv = array->AppendElement(slot, false);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
     }
   }
-  SECMOD_ReleaseReadLock(lock);
-  rv = array->Enumerate(_retval);
-  return rv;
+
+  return array->Enumerate(_retval);
 }
 
 NS_IMPL_ISUPPORTS(nsPKCS11ModuleDB, nsIPKCS11ModuleDB, nsICryptoFIPSInfo)
 
 nsPKCS11ModuleDB::nsPKCS11ModuleDB()
 {
 }
 
@@ -412,45 +413,47 @@ nsPKCS11ModuleDB::FindSlotByName(const c
   if (!slotinfo)
     return NS_ERROR_FAILURE;
   nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
   PK11_FreeSlot(slotinfo);
   slot.forget(_retval);
   return NS_OK;
 }
 
-NS_IMETHODIMP 
-nsPKCS11ModuleDB::ListModules(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  nsresult rv = NS_OK;
-  /* get isupports array */
-  nsCOMPtr<nsISupportsArray> array;
-  rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) return rv;
-  /* get the default list of modules */
-  SECMODModuleList *list = SECMOD_GetDefaultModuleList();
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
+
   /* lock down the list for reading */
-  SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
-  SECMOD_GetReadLock(lock);
-  while (list) {
+  AutoSECMODListReadLock lock;
+  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
+       list = list->next) {
     nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
-    array->AppendElement(module);
-    list = list->next;
+    nsresult rv = array->AppendElement(module, false);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
+
   /* Get the modules in the database that didn't load */
-  list = SECMOD_GetDeadModuleList();
-  while (list) {
+  for (SECMODModuleList* list = SECMOD_GetDeadModuleList(); list;
+       list = list->next) {
     nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
-    array->AppendElement(module);
-    list = list->next;
+    nsresult rv = array->AppendElement(module, false);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
-  SECMOD_ReleaseReadLock(lock);
-  rv = array->Enumerate(_retval);
-  return rv;
+
+  return array->Enumerate(_retval);
 }
 
 NS_IMETHODIMP nsPKCS11ModuleDB::GetCanToggleFIPS(bool *aCanToggleFIPS)
 {
   nsNSSShutDownPreventionLock locker;
   *aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
   return NS_OK;
 }
--- a/security/manager/ssl/nsPKCS11Slot.h
+++ b/security/manager/ssl/nsPKCS11Slot.h
@@ -1,25 +1,25 @@
 /* -*- 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 __NS_PKCS11SLOT_H__
-#define __NS_PKCS11SLOT_H__
+#ifndef nsPKCS11Slot_h
+#define nsPKCS11Slot_h
 
-#include "nsISupports.h"
-#include "nsIPKCS11Slot.h"
+#include "nsICryptoFIPSInfo.h"
 #include "nsIPKCS11Module.h"
 #include "nsIPKCS11ModuleDB.h"
-#include "nsICryptoFIPSInfo.h"
+#include "nsIPKCS11Slot.h"
+#include "nsISupports.h"
+#include "nsNSSShutDown.h"
 #include "nsString.h"
 #include "pk11func.h"
-#include "nsNSSShutDown.h"
 
 class nsPKCS11Slot : public nsIPKCS11Slot,
                      public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPKCS11SLOT
 
@@ -72,9 +72,28 @@ protected:
   virtual ~nsPKCS11ModuleDB();
   /* additional members */
 };
 
 #define NS_PKCS11MODULEDB_CID \
 { 0xff9fbcd7, 0x9517, 0x4334, \
   { 0xb9, 0x7a, 0xce, 0xed, 0x78, 0x90, 0x99, 0x74 }}
 
-#endif
+class MOZ_RAII AutoSECMODListReadLock final
+{
+public:
+  AutoSECMODListReadLock()
+    : mLock(SECMOD_GetDefaultModuleListLock())
+  {
+    MOZ_ASSERT(mLock, "Should have the default SECMOD lock");
+    SECMOD_GetReadLock(mLock);
+  }
+
+  ~AutoSECMODListReadLock()
+  {
+    SECMOD_ReleaseReadLock(mLock);
+  }
+
+private:
+  SECMODListLock* mLock;
+};
+
+#endif // nsPKCS11Slot_h
--- a/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
@@ -11,26 +11,48 @@
 // has been succssfully observed, and then it unloads the test module.
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
 
 const gExpectedTokenLabel = "Test PKCS11 TokeƱ Label";
 
+const gTokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
+                   .getService(Ci.nsIPK11TokenDB);
+
 function SmartcardObserver(type) {
   this.type = type;
   do_test_pending();
 }
 
 SmartcardObserver.prototype = {
   observe: function(subject, topic, data) {
     equal(topic, this.type, "Observed and expected types should match");
     equal(gExpectedTokenLabel, data,
           "Expected and observed token labels should match");
+
+    // Test that the token list contains the test token only when the test
+    // module is loaded.
+    // Note: This test is located here out of convenience. In particular,
+    //       observing the "smartcard-insert" event is the only time where it
+    //       is reasonably certain for the test token to be present (see the top
+    //       level comment for this file for why).
+    let tokenList = gTokenDB.listTokens();
+    let testTokenLabelFound = false;
+    while (tokenList.hasMoreElements()) {
+      let token = tokenList.getNext().QueryInterface(Ci.nsIPK11Token);
+      if (token.tokenLabel == gExpectedTokenLabel) {
+        testTokenLabelFound = true;
+        break;
+      }
+    }
+    equal(testTokenLabelFound, this.type == "smartcard-insert",
+          "Should find test token only when the test module is loaded");
+
     Services.obs.removeObserver(this, this.type);
     do_test_finished();
   }
 };
 
 function run_test() {
   Services.obs.addObserver(new SmartcardObserver("smartcard-insert"),
                            "smartcard-insert", false);
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_list.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods for listing PKCS #11 modules and slots via loading and
+// unloading a test PKCS #11 module.
+
+// Note: Tests for listing PKCS #11 tokens are located in
+//       test_pkcs11_insert_remove.js out of convenience.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
+                    .getService(Ci.nsIPKCS11ModuleDB);
+
+function checkTestModuleNotPresent() {
+  let modules = gModuleDB.listModules();
+  ok(modules.hasMoreElements(),
+     "One or more modules should be present with test module not present");
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+    notEqual(module.name, "PKCS11 Test Module",
+             "Non-test module name shouldn't equal 'PKCS11 Test Module'");
+  }
+}
+
+/**
+ * Checks that the test module exists in the module list.
+ *
+ * @returns {nsIPKCS11Module}
+ *          The test module.
+ */
+function checkTestModuleExists() {
+  let modules = gModuleDB.listModules();
+  ok(modules.hasMoreElements(),
+     "One or more modules should be present with test module present");
+  let testModule = null;
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+    if (module.name == "PKCS11 Test Module") {
+      testModule = module;
+      break;
+    }
+  }
+  notEqual(testModule, null, "Test module should have been found");
+
+  return testModule;
+}
+
+function run_test() {
+  let libraryName = ctypes.libraryName("pkcs11testmodule");
+  let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
+  libraryFile.append("pkcs11testmodule");
+  libraryFile.append(libraryName);
+  ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
+
+  // Check that if we have never added the test module, that we don't find it
+  // in the module list.
+  checkTestModuleNotPresent();
+
+  // Check that adding the test module makes it appear in the module list.
+  let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11);
+  do_register_cleanup(() => {
+    try {
+      pkcs11.deleteModule("PKCS11 Test Module");
+    } catch (e) {
+      // deleteModule() throws if the module we tell it to delete is missing,
+      // or if some other thing went wrong. Since we're just cleaning up,
+      // there's nothing to do even if the call fails. In addition, we delete
+      // the test module during a normal run of this test file, so we need to
+      // catch the exception that is raised to not have the test fail.
+    }
+  });
+  pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0);
+  let testModule = checkTestModuleExists();
+
+  // Check that listing the slots for the test module works.
+  let slots = testModule.listSlots();
+  let testModuleSlotCount = 0;
+  while (slots.hasMoreElements()) {
+    let slot = slots.getNext().QueryInterface(Ci.nsIPKCS11Slot);
+    equal(slot.name, "Test PKCS11 Slot",
+          "Test module slot should have correct name");
+    testModuleSlotCount++;
+  }
+  equal(testModuleSlotCount, 1, "Test module should only have one slot");
+
+  // Check that deleting the test module makes it disappear from the module list.
+  pkcs11.deleteModule("PKCS11 Test Module");
+  checkTestModuleNotPresent();
+}
--- a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
+++ b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
@@ -3,14 +3,17 @@ head = head_psm.js
 tail =
 tags = psm
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
 
 [test_pkcs11_insert_remove.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"
+[test_pkcs11_list.js]
+# Bug 1049969: this test doesn't work on windows
+skip-if = os == "win"
 [test_pkcs11_no_events_after_removal.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"
 [test_pkcs11_safe_mode.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -29,16 +29,22 @@
           }
         ],
         "service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html": [
           {
             "path": "service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html",
             "url": "/_mozilla/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html"
           }
         ],
+        "service-workers/service-worker/activate-event-after-install-state-change.https.html": [
+          {
+            "path": "service-workers/service-worker/activate-event-after-install-state-change.https.html",
+            "url": "/_mozilla/service-workers/service-worker/activate-event-after-install-state-change.https.html"
+          }
+        ],
         "service-workers/service-worker/activation-after-registration.https.html": [
           {
             "path": "service-workers/service-worker/activation-after-registration.https.html",
             "url": "/_mozilla/service-workers/service-worker/activation-after-registration.https.html"
           }
         ],
         "service-workers/service-worker/active.https.html": [
           {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Service Worker: registration events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+promise_test(function(t) {
+  var script = 'resources/empty-worker.js';
+  var scope = 'resources/blank.html';
+  var registration;
+
+  return service_worker_unregister_and_register(t, script, scope)
+    .then(function(registration) {
+        var sw = registration.installing;
+
+        return new Promise(t.step_func(function(resolve) {
+          sw.onstatechange = t.step_func(function() {
+            if (sw.state === 'installed') {
+              assert_equals(registration.active, null,
+                            'installed event should be fired before activating service worker');
+              resolve();
+            }
+          });
+        }));
+      })
+    .then(function() {
+        return service_worker_unregister_and_done(t, scope);
+      })
+    .catch(unreached_rejection(t));
+  }, 'installed event should be fired before activating service worker');
+
+</script>
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/performance-timeline-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/performance-timeline-worker.js
@@ -2,44 +2,35 @@ importScripts('/resources/testharness.js
 
 promise_test(function(test) {
     var durationMsec = 100;
     // There are limits to our accuracy here.  Timers may fire up to a
     // millisecond early due to platform-dependent rounding.  In addition
     // the performance API introduces some rounding as well to prevent
     // timing attacks.
     var accuracy = 1.5;
-    var dateStart;
     return new Promise(function(resolve) {
-        dateStart = Date.now();
         performance.mark('startMark');
         setTimeout(resolve, durationMsec);
       }).then(function() {
           performance.mark('endMark');
-          var dateEnd = Date.now();
           performance.measure('measure', 'startMark', 'endMark');
           var startMark = performance.getEntriesByName('startMark')[0];
           var endMark = performance.getEntriesByName('endMark')[0];
           var measure = performance.getEntriesByType('measure')[0];
           assert_equals(measure.startTime, startMark.startTime);
           assert_approx_equals(endMark.startTime - startMark.startTime,
                                measure.duration, 0.001);
           assert_greater_than(measure.duration, durationMsec - accuracy);
           assert_equals(performance.getEntriesByType('mark').length, 2);
           assert_equals(performance.getEntriesByType('measure').length, 1);
           performance.clearMarks('startMark');
           performance.clearMeasures('measure');
           assert_equals(performance.getEntriesByType('mark').length, 1);
           assert_equals(performance.getEntriesByType('measure').length, 0);
-
-          // Performance API's fuzzy resolution means its measured duration
-          // can round to a slightly different value compared to Date.now()
-          // measurements.
-          var dateDuration = dateEnd - dateStart;
-          assert_approx_equals(Math.round(measure.duration), dateDuration, 1);
       });
   }, 'User Timing');
 
 promise_test(function(test) {
     return fetch('dummy.txt')
       .then(function(resp) {
           return resp.text();
         })
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
@@ -1,33 +1,24 @@
 self.state = 'starting';
 
 self.addEventListener('install', function() {
     self.state = 'installing';
   });
 
-self.addEventListener('activate', function() {
-    self.state = 'activating';
-  });
-
 self.addEventListener('message', function(event) {
     var port = event.data.port;
     if (self.state !== 'installing') {
       port.postMessage('FAIL: Worker should be waiting in installed state');
       return;
     }
     self.skipWaiting()
       .then(function(result) {
           if (result !== undefined) {
             port.postMessage('FAIL: Promise should be resolved with undefined');
             return;
           }
-          if (self.state !== 'activating') {
-            port.postMessage(
-                'FAIL: Promise should be resolved after worker activated');
-            return;
-          }
           port.postMessage('PASS');
         })
       .catch(function(e) {
           port.postMessage('FAIL: unexpected exception: ' + e);
         });
   });
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html
@@ -5,52 +5,56 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <script>
 
 promise_test(function(t) {
     var scope = 'resources/blank.html';
     var url1 = 'resources/empty.js';
     var url2 = 'resources/skip-waiting-installed-worker.js';
-    var frame, frame_sw, service_worker, onmessage, oncontrollerchanged;
+    var frame, frame_sw, service_worker, registration, onmessage, oncontrollerchanged;
     var saw_message = new Promise(function(resolve) {
         onmessage = function(e) {
             var message = e.data;
             assert_equals(
                 message, 'PASS',
-                'skipWaiting promise should be resolved after activated');
+                'skipWaiting promise should be resolved with undefined');
+
+            assert_equals(registration.active.scriptURL, normalizeURL(url2),
+                          "skipWaiting should make worker become active");
             resolve();
         };
       });
     var saw_controllerchanged = new Promise(function(resolve) {
         oncontrollerchanged = function() {
             assert_equals(
                 frame_sw.controller.scriptURL, normalizeURL(url2),
                 'Controller scriptURL should change to the second one');
             resolve();
         };
       });
     return service_worker_unregister_and_register(t, url1, scope)
-      .then(function(registration) {
-          return wait_for_state(t, registration.installing, 'activated');
+      .then(function(r) {
+          return wait_for_state(t, r.installing, 'activated');
         })
       .then(function() {
           return with_iframe(scope);
         })
       .then(function(f) {
           frame = f;
           frame_sw = f.contentWindow.navigator.serviceWorker;
           assert_equals(
               frame_sw.controller.scriptURL, normalizeURL(url1),
               'Document controller scriptURL should equal to the first one');
           frame_sw.oncontrollerchange = t.step_func(oncontrollerchanged);
           return navigator.serviceWorker.register(url2, {scope: scope});
         })
-      .then(function(registration) {
-          service_worker = registration.installing;
+      .then(function(r) {
+          registration = r;
+          service_worker = r.installing;
           return wait_for_state(t, service_worker, 'installed');
         })
       .then(function() {
           var channel = new MessageChannel();
           channel.port1.onmessage = t.step_func(onmessage);
           service_worker.postMessage({port: channel.port2}, [channel.port2]);
           return Promise.all([saw_message, saw_controllerchanged]);
         })
--- a/testing/web-platform/tests/fetch/api/request/request-init-002.html
+++ b/testing/web-platform/tests/fetch/api/request/request-init-002.html
@@ -18,37 +18,47 @@
         var headers = new Headers(headerDict);
         var request = new Request("", { "headers" : headers })
         for (var name in headerDict) {
           assert_equals(request.headers.get(name), headerDict[name],
            "request's headers has " + name + " : " + headerDict[name]);
         }
       }, "Initialize Request with headers values");
 
+      function makeRequestInit(body, method) {
+        return {"method": method, "body": body};
+      }
+
       function checkRequestInit(body, bodyType, expectedTextBody) {
         promise_test(function(test) {
-          var request = new Request("", {"method": "POST", "body": body});
-          assert_throws(new TypeError(),
-                        function() { new Request("", {"method": "GET", "body": body}); }
-          );
+          var request = new Request("", makeRequestInit(body, "POST"));
+          if (body) {
+            assert_throws(new TypeError(),
+                          function() { new Request("", makeRequestInit(body, "GET")); }
+            );
+          } else {
+            new Request("", makeRequestInit(body, "GET")); // should not throw
+          }
           var reqHeaders = request.headers;
           var mime = reqHeaders.get("Content-Type");
-          assert_true(mime && mime.search(bodyType) > -1, "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\"");
+          assert_true(!body || (mime && mime.search(bodyType) > -1), "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\"");
           return request.text().then(function(bodyAsText) {
             //not equals: cannot guess formData exact value
             assert_true( bodyAsText.search(expectedTextBody) > -1, "Retrieve and verify request body");
           });
         }, "Initialize Response's body with " + bodyType);
       }
 
       var blob = new Blob(["This is a blob"], {type: "application/octet-binary"});
       var formaData = new FormData();
       formaData.append("name", "value");
       var usvString = "This is a USVString"
 
+      checkRequestInit(undefined, undefined, "");
+      checkRequestInit(null, null, "");
       checkRequestInit(blob, "application/octet-binary", "This is a blob");
       checkRequestInit(formaData, "multipart/form-data", "name=\"name\"\r\n\r\nvalue");
       checkRequestInit(usvString, "text/plain;charset=UTF-8", "This is a USVString");
 
       // Ensure test does not time out in case of missing URLSearchParams support.
       if (window.URLSearchParams) {
         var urlSearchParams = new URLSearchParams("name=value");
         checkRequestInit(urlSearchParams, "application/x-www-form-urlencoded;charset=UTF-8", "name=value");
--- a/tools/mercurial/eslintvalidate.py
+++ b/tools/mercurial/eslintvalidate.py
@@ -2,17 +2,17 @@
 # GNU General Public License version 2 or any later version.
 
 import os
 import sys
 import re
 import json
 from subprocess import check_output, CalledProcessError
 
-lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml)$')
+lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml|html)$')
 ignored = "File ignored because of your .eslintignore file. Use --no-ignore to override."
 
 def is_lintable(filename):
     return lintable.match(filename)
 
 def display(ui, output):
     results = json.loads(output)
     for file in results:
@@ -30,16 +30,16 @@ def eslinthook(ui, repo, node=None, **op
 
     deleted = repo.status(ctx.p1().node(), ctx.node()).deleted
     files = [f for f in ctx.files() if f not in deleted and is_lintable(f)]
 
     if len(files) == 0:
         return
 
     try:
-        output = check_output(["eslint", "--format", "json"] + files)
+        output = check_output(["eslint", "--format", "json", "--plugin", "html"] + files)
         display(ui, output)
     except CalledProcessError as ex:
         display(ui, ex.output)
         ui.warn("ESLint found problems in your changes, please correct them.\n")
 
 def reposetup(ui, repo):
     ui.setconfig('hooks', 'commit.eslint', eslinthook)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6154,16 +6154,18 @@ void nsWindow::OnWindowPosChanged(WINDOW
     // handle it.
     if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
       return;
 
     WINDOWPLACEMENT pl;
     pl.length = sizeof(pl);
     ::GetWindowPlacement(mWnd, &pl);
 
+    nsSizeMode previousSizeMode = mSizeMode;
+
     // Windows has just changed the size mode of this window. The call to
     // SizeModeChanged will trigger a call into SetSizeMode where we will
     // set the min/max window state again or for nsSizeMode_Normal, call
     // SetWindow with a parameter of SW_RESTORE. There's no need however as
     // this window's mode has already changed. Updating mSizeMode here
     // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
     // to window docking. (bug 489258)
     if (pl.showCmd == SW_SHOWMAXIMIZED)
@@ -6198,17 +6200,17 @@ void nsWindow::OnWindowPosChanged(WINDOW
                  ("*** mSizeMode: nsSizeMode_Maximized\n"));
         break;
       default:
           MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** mSizeMode: ??????\n"));
         break;
     }
 #endif
 
-    if (mWidgetListener)
+    if (mWidgetListener && mSizeMode != previousSizeMode)
       mWidgetListener->SizeModeChanged(mSizeMode);
 
     // If window was restored, window activation was bypassed during the 
     // SetSizeMode call originating from OnWindowPosChanging to avoid saving
     // pre-restore attributes. Force activation now to get correct attributes.
     if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
       DispatchFocusToTopLevelWindow(true);
 
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -632,18 +632,16 @@
   ERROR(NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED,   FAILURE(48)),
   ERROR(NS_ERROR_XPC_BAD_ID_STRING,                    FAILURE(49)),
   ERROR(NS_ERROR_XPC_BAD_INITIALIZER_NAME,             FAILURE(50)),
   ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN,                FAILURE(51)),
   ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN,           FAILURE(52)),
   ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL,   FAILURE(53)),
   ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,         FAILURE(54)),
   /* any new errors here should have an associated entry added in xpc.msg */
-
-  ERROR(NS_SUCCESS_I_DID_SOMETHING,      SUCCESS(1)),
 #undef MODULE
 
 
   /* ======================================================================= */
   /* 19: NS_ERROR_MODULE_PROFILE */
   /* ======================================================================= */
 #define MODULE NS_ERROR_MODULE_PROFILE
   ERROR(NS_ERROR_LAUNCHED_CHILD_PROCESS,  FAILURE(200)),
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -360,16 +360,21 @@ nsWebShellWindow::SizeModeChanged(nsSize
         ourWindow->SetFullScreen(false);
       }
     }
 
     // And always fire a user-defined sizemodechange event on the window
     ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange"));
   }
 
+  nsIPresShell* presShell;
+  if ((presShell = GetPresShell())) {
+    presShell->GetPresContext()->SizeModeChanged(sizeMode);
+  }
+
   // Note the current implementation of SetSizeMode just stores
   // the new state; it doesn't actually resize. So here we store
   // the state and pass the event on to the OS. The day is coming
   // when we'll handle the event here, and the return result will
   // then need to be different.
 }
 
 void